UNPKG

10.5 kBMarkdownView Raw
1**experimental/unstable** api changes will still occur (without deprecation warnings)
2
3`npm install ipso` 0.0.10 [license](./license)
4
5
6Decorator, for testing, with [Mocha](https://github.com/visionmedia/mocha)
7
8All examples in [coffee-script](http://coffeescript.org/).
9
10
11What is this `ipso` thing?
12--------------------------
13
14[The Short Answer](https://github.com/nomilous/vertex/commit/a4b0ef4c6bc14874f5b7d8ff3e5bcbcf4d45edc6)
15
16The Long Answer, ↓
17
18### (test/) Injection Decorator
19
20It is placed in front of the test functions.
21
22```coffee
23ipso = require 'ipso'
24
25it 'does something', ipso (done) ->
26
27 done() # as usual
28
29```
30
31It can inject node modules into suites.
32
33```coffee
34
35describe 'it can inject into describe', ipso (vm) ->
36 context 'it can inject into context', ipso (net) ->
37 it 'confirms', ->
38
39 vm.should.equal require 'vm'
40 net.should.equal require 'net'
41
42```
43
44It can inject node modules into tests.
45
46```coffee
47
48it 'does something', ipso (done, http) ->
49
50 http.should.equal require 'http'
51
52```
53
54**IMPORTANT**: `done` will only contain the test resolver if the argument's signaure is literally "done" and it in the first position.
55
56In other words.
57
58```coffee
59
60it 'does something', ipso (finished, http) ->
61
62#
63# => Error: Cannot find module 'finished'
64#
65# And the problem becomes more subtle if there IS a module called 'finshed' installed...
66#
67
68```
69
70
71It defines `.does()` on each injected module for use as a **stubber**.
72
73```coffee
74
75it 'creates an http server', ipso (done, http) ->
76
77 http.does
78 createServer: ->
79 anotherFunction: ->
80
81 http.createServer()
82 done()
83
84```
85
86It uses mocha's JSON diff to display failure to call the stubbed function.
87
88```json
89
90 actual expected
91
92 1 | {
93 2 | "http": {
94 3 | "functions": {
95 4 | "Object.createServer()": "was called"
96 5 | "Object.anotherFunction()": "was NOT called"
97 6 | }
98 7 | }
99 8 | }
100
101```
102
103The stub replaces the actual function on the module and can therefore return a suitable mock.
104
105```coffee
106http = require 'http'
107class MyServer
108 listen: (opts, handler) ->
109 http.createServer(handler).listen opts.port
110```
111
112```coffee
113{ipso, mock} = require 'ipso'
114
115it 'creates an http server and listens at opts.port', ipso (done, http, MyServer) ->
116
117 http.does
118 createServer: ->
119 return mock('server').does
120 listen: (port) ->
121 port.should.equal 3000
122 done()
123
124 MyServer.listen port: 3000, (req, res) ->
125
126```
127
128You may have noticed that `MyServer` was also injected in the previous example.
129
130* The injector recurses `./lib` and `./app` for the specified module.
131* It does so only if the module has a `CamelCaseModuleName` in the injection argument's signature
132* It searches for the underscored equivalent `./lib/**/*/camel_case_module_name.js|coffee`
133* These **Local Module Injections** can also be stubbed.
134
135
136It can create multiple function expectation stubs ( **and spies** ).
137
138```coffee
139
140it 'can create multiple expectation stubs', ipso (done, Server) ->
141
142 Server.does
143
144 _listen: ->
145
146 # console.log arguments
147
148 console.log """
149
150 _underscore denotes a spy function
151 ==================================
152
153 * the original will be called after the spy (this function)
154 * both will receive the same arguments
155
156 """
157
158 anotherFunction: ->
159
160 Server.start()
161
162
163```
164
165
166**PENDING (unlikely, use tags, see below)** It can create future instance stubs (on the prototype)
167
168```coffee
169
170it 'can create multiple expectation stubs', ipso (done, Periscope, events, should) ->
171
172 # Periscope.$prototype.does (dunno yet)
173 Periscope.prototype.does
174
175 measureDepth: -> return 30
176
177 _riseToSurface: (distance, finishedRising) ->
178 distance.should.equal 30
179
180 _openLens: ->
181 @videoStream.codec.should.equal πr²
182
183 #
184 # note: That `@` a.k.a. `this` refers to the instance context
185 # and not the test context. It therefore has access to
186 # properties of the Periscope instance.
187 #
188
189
190 periscope = new Periscope codec: πr²
191 periscope.up (error, eyehole) ->
192
193 should.not.exist error
194 eyehole.should.be.an.instanceof events.EventEmitter
195 done()
196
197```
198
199It supports injection of non-js-eval-able module names or cases where the local module search fails
200
201
202```coffee
203
204ipso = require('ipso').modules
205 engine:
206 require: 'engine.io'
207 Proxy: #
208 require: './lib/proxy/server' # * Because the filenames are the same
209 Core: # so injecting `Server` will fail.
210 require: './lib/core/server' #
211 #
212...
213
214 it 'can inject by config', ipso (done, engine, Proxy, Core) ->
215
216 #
217 # ...
218 #
219
220```
221* IMPORTANT
222 * `require` in the above config is a subkey, and **not the require function itself**
223 * The path should be relative to `process.cwd()`, not `__dirname`
224
225
226**PARTIALLY PENDING** It supports tagged objects for multiple subsequent injections.
227
228```coffee
229
230context 'creates tagged objects for injection into multiple nested tests', ->
231
232 before ipso (done, ClassName) ->
233
234 ipso.tag
235
236 instanceA: new ClassName 'type A'
237 instanceB: new ClassName 'type B'
238 client: require 'socket.io-client'
239
240 .then done
241
242 it 'can test with them', (instanceA, instanceB, client) ->
243 it 'and again', (instanceA, instanceB) ->
244
245```
246
247
248### Complex Usage
249
250It can create active mocks for fullblown mocking and stubbing
251
252```coffee
253
254beforeEach ipso (done, http) ->
255
256 http.does
257 createServer: (handler) =>
258 process.nextTick ->
259
260 #
261 # mock an actual "hit"
262 #
263
264 handler mock('req'), mock('mock response').does
265
266 writeHead: ->
267 write: ->
268 end: ->
269
270 return ipso.mock( 'mock server' ).does
271
272 listen: (@port, args...) =>
273 address: -> 'mock address object'
274
275 #
276 # note: '=>' pathway from hook's root scope means @port
277 # refers to the `this` of the hook's root scope - which
278 # is shared with the tests themselves, so @port becomes
279 # available in all tests that are preceeded by this hook
280 #
281
282it 'creates a server, starts listening and responds when hit', ipso (facto, http) ->
283
284 server = http.createServer (req, res) ->
285
286 res.writeHead 200
287 res.end()
288 facto()
289
290 server.listen 3000
291 @port.should.equal 3000
292
293```
294```json
295
296 actual expected
297
298 1 | {
299 2 | "http": {
300 3 | "functions": {
301 4 | "Object.createServer()": "was called"
302 5 | }
303 6 | },
304 7 | "mock server": {
305 8 | "functions": {
306 9 | "Object.listen()": "was called",
307 10 | "Object.address()": "was called"
308 11 | }
309 12 | },
310 13 | "mock response": {
311 14 | "functions": {
312 15 | "Object.writeHead()": "was called",
313 16 | "Object.write()": "was NOT called", <--------------------
314 17 | "Object.end()": "was called"
315 18 | }
316 19 | }
317 20 | }
318
319```
320
321
322```coffee
323before ipso (done, should) ->
324
325 tag
326
327 Got: should.exist
328 Not: should.not.exist
329
330 .then done
331
332it 'has the vodka and the olive', ipso (martini, Got, Not) ->
333
334 Got martini.vodka
335 Got martini.olive
336 Not martini.gin
337
338 #
339 # * there is great value in using **only** local scope in test... (!)
340 #
341
342```
343
344
345It supports promises.
346
347```coffee
348
349it 'fails the test on the first rejection in the chain', ipso (facto, Module) ->
350
351 Module.functionThatReturnsAPromise()
352
353 .then -> Module.functionThatReturnsAPromise()
354 .then -> Module.functionThatReturnsAPromise()
355 .then -> Module.functionThatReturnsAPromise()
356 .then -> facto()
357
358```
359
360Ipso Facto
361
362```coffee
363
364it 'does many things to come', ipso (facto, ...) ->
365
366 facto[MetaThings]()
367
368 #
369 # facto() calls mocha's done() in the background
370 #
371
372```
373
374What MetaThings?
375
376* well, ... (( the brief brainstorm suggested a Planet-sized Plethora of Particularly Peachy Possibilities Perch Patiently Poised Pending a Plunge into **That** rabbit hole.
377
378
379And who is Unthahorsten?
380
381* And why was he doing the equivalent of standing in the equivalent of a laboratory.
382
383
384cli
385---
386
387* There is a cli.
388* It is tailored fairly tightly to my size and shape of process.
389
390**However**, there are options:
391
392```
393
394 Usage: ipso [options]
395
396 Options:
397
398 -h, --help output usage information
399 -V, --version output the version number
400 -w, --no-watch Dont watch spec and src dirs.
401 -n, --no-env Dont load .env.test
402 -m, --mocha Use mocha.
403 -e, --alt-env [name] Loads .env.name
404 --spec [dir] Specify alternate spec dir.
405 --src [dir] Specify alternate src dir.
406 --lib [dir] Specify alternate compile target.
407
408```
409
410### Highlight
411
412It can quickly start up a node-inspector session on a v8 debugger socket. It may at some point seamlessly attach to the running tests, with `Module.does(...)` specifying breakpoints. (that would be very! very! nice...)
413
414```
415$ ipso --mocha -e name
416ipso: warning: .env.name is PRODUCTION
417ipso: loaded .env.name
418ipso: watching directory: ./spec
419ipso: watching directory: ./src
420>
421>
422> inspect 3001 5860 lib/examples/basic.js
423>
424> debugger listening on port 5860 <----------------------------
425> Node Inspector v0.5.0
426> info: socket.io started
427> Visit http://127.0.0.1:3001/debug?port=5860 to start debugging.
428> =====================================
429```
430
431### Specific!ness
432
433It watches for ./src and ./spec changes and runs the changed.
434
435`ipso --mocha --src [dir] --spec [dir] --lib [dir]`
436
437* ./src changes will be compiled into ./lib/...
438* the corresponding test will then be run from ./spec/...
439* the followingly illustrated "path echo" **is assumed to ALWAYS be the case**
440
441```
442 src/same/dirname/source_name.coffee
443spec/same/dirname/source_name_spec.coffee
444```