Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 142 lines (103 sloc) 4.604 kB
14df9ac @vojtajina Add travis ci icon to readme
authored
1 # Node Mocks [![Build Status](https://secure.travis-ci.org/vojtajina/node-mocks.png?branch=master)](http://travis-ci.org/vojtajina/node-mocks)
c6b2c48 @vojtajina Add readme and simple example
authored
2
3 Set of mocks and utilities for easier unit testing with [Node.js].
4
5 See http://howtonode.org/testing-private-state-and-mocking-deps for better explanation.
6
7 ## Prerequisites
8
9 * [Node.js]
10 * [NPM] (shipped with Node since 0.6.3)
11
12
13 ## Installation
14
15 sudo npm install -g mocks
16
17 # or install in local folder
18 npm install mocks
19
20
21 ## Example
22
23 ### Mocking a file system
24
25 ````javascript
26 // during unit test - we inject mock file system instead of real fs
27 var fs = require('fs');
28
29 // even if this function is not public, we can test it directly
30 var filterOnlyExistingFiles = function(collection, done) {
31 var filtered = [],
32 waiting = 0;
33
34 collection.forEach(function(file) {
35 waiting++;
36 fs.stat(file, function(err, stat) {
37 if (!err && stat.isFile()) filtered.push(file);
38 waiting--;
39 if (!waiting) done(null, filtered);
40 });
41 });
42 };
43 ````
44
45 ````coffeescript
46 # simple unit test (jasmine syntax, coffescript)
47 describe 'app', ->
48 mocks = require 'mocks'
49 loadFile = mocks.loadFile
50 app = done = null
51
52 beforeEach ->
53 done = jasmine.createSpy 'done'
54 # load the file and inject fake fs module
55 app = loadFile __dirname + '/app.js',
56 fs: mocks.fs.create
57 'bin':
58 'run.sh': 1,
59 'install.sh': 1
60 'home':
61 'some.js': 1,
62 'another.txt': 1
63 'one.js': 1,
64 'two.js': 1,
65 'three.js': 1
66
67
68 it 'should return only existing files', ->
69 done.andCallFake (err, filtered) ->
70 expect(err).toBeFalsy()
71 expect(filtered).toEqual ['/bin/run.sh']
72
73 app.filterOnlyExistingFiles ['/bin/run.sh', '/non.txt'], done
74 waitsFor -> done.callCount
75
76 it 'should ignore directories', ->
77 done.andCallFake (err, filtered) ->
78 expect(filtered).toEqual ['/bin/run.sh', '/home/some.js']
79
80 app.filterOnlyExistingFiles ['/bin/run.sh', '/home', '/home/some.js'], done
81 waitsFor -> done.callCount
82 ````
83
84 ### Faking randomness
85 Non-blocking I/O operations can return in random order. Let's say you read a content of two files (asynchronously). There is no guarantee, that you get the content in right order. That's fine, but we want to test our code, whether it can handle such a situation and still work properly. In that case, you can use `predictableNextTick`, which process callbacks depending on given pattern.
86
87 ````coffeescript
88
89 it 'should preserve order', ->
90 done.andCallFake (err, filtered) ->
91 expect(filtered).toEqual ['/one.js', '/two.js', '/three.js']
92
93 app.filterOnlyExistingFiles ['/one.js', '/two.js', '/three.js'], done
94 waitsFor -> done.callCount
95 ````
96 This test will always pass. That's cool, as we like to see tests passing. The bad thing is, that it does not work in production, with real file system, as it might return in different order...
97 So, we need to test, whether our app works even when the `fs` returns in random order. Having randomness in unit tests is not good habit, as it leads to flaky tests.
98 Let's change the previous unit test to this:
99
100 ````coffeescript
101 it 'should preserve order', ->
102 done.andCallFake (err, filtered) ->
103 expect(filtered).toEqual ['/one.js', '/two.js', '/three.js']
104
105 mocks.predictableNextTick.pattern = [2, 0, 1]
106 app.filterOnlyExistingFiles ['/one.js', '/two.js', '/three.js'], done
107 waitsFor -> done.callCount
108 ````
109 Now, the unit test fails, because our fake file system calls back in different order. Note, it's not random, as you explicitly specified the pattern (2, 0, 1), so it the fake fs will consistently call back in this order: /three.js, /one.js, two.js.
110
111
112 ## API
70ca473 @vojtajina Readme - change SlimJim to Testacular
authored
113 Currently supported API is only very small part of real [Node's API]. Basically I only implemented methods I need for testing [Testacular].
c6b2c48 @vojtajina Add readme and simple example
authored
114
115 I will keep adding more and of course if anyone wants to help - pull requests are more than welcomed.
116
688aa82 @vojtajina Add trivial mock for node-glob
authored
117 ### [fs](http://nodejs.org/api/fs.html)
c6b2c48 @vojtajina Add readme and simple example
authored
118
119 - stat(path, callback)
120 - readdir(path, callback)
121 - readFile(path [, encoding], callback)
122 - readFileSync(path)
123 - watchFile(path [, options], callback)
124 - _touchFile(path, mtime, content) *
125
688aa82 @vojtajina Add trivial mock for node-glob
authored
126 ### [http](http://nodejs.org/api/http.html)
c6b2c48 @vojtajina Add readme and simple example
authored
127
128 - http.ServerResponse
129 - http.ServerRequest
130
688aa82 @vojtajina Add trivial mock for node-glob
authored
131 ### [glob](https://github.com/isaacs/node-glob)
132
c6b2c48 @vojtajina Add readme and simple example
authored
133
c319861 @vojtajina Update loadFile - allow passing globals, add some unit tests
authored
134 ### loadFile(path [, mocks] [, globals])
c6b2c48 @vojtajina Add readme and simple example
authored
135 ### predictableNextTick(fn)
136 ### predictableNextTick.pattern
137
138 [Node.js]: http://nodejs.org/
139 [NPM]: http://npmjs.org/
140 [Node's API]: http://nodejs.org/docs/latest/api/index.html
70ca473 @vojtajina Readme - change SlimJim to Testacular
authored
141 [Testacular]: https://github.com/vojtajina/testacular
Something went wrong with that request. Please try again.