1 | # Chai Spies
|
2 |
|
3 | This is an addon plugin for the [chai](https://github.com/chaijs/chai) assertion library. It provides the
|
4 | most basic function spy ability and tests.
|
5 |
|
6 | This library is primarily meant to serve as a starting point for anyone interested in developing chai plugins. If
|
7 | developing a module, you are welcome to use this as a starting point. I also encourage the use of the compile
|
8 | tools to allow modules to work both in node.js and the browser.
|
9 |
|
10 | ## Installation
|
11 |
|
12 | #### Node.js
|
13 |
|
14 | Chai spies are available on npm.
|
15 |
|
16 | $ npm install chai-spies
|
17 |
|
18 | #### Browser
|
19 |
|
20 | Include `chai-spies.js` after including `chai.js`.
|
21 |
|
22 | ```xml
|
23 | <script src="chai-spies.js"></script>
|
24 | ```
|
25 |
|
26 | ## Plug In
|
27 |
|
28 | If you are using `chai-spies` in the browser, there is nothing you need to do. It will detect `chai` in the global
|
29 | namespace and automatically get used.
|
30 |
|
31 | If you are using node, here is a useful bit.
|
32 |
|
33 | ```js
|
34 | const chai = require('chai')
|
35 | , spies = require('chai-spies');
|
36 |
|
37 | chai.use(spies);
|
38 |
|
39 | const should = chai.should()
|
40 | , expect = chai.expect;
|
41 | ```
|
42 |
|
43 | ## TypeScript Setup
|
44 |
|
45 | If using TypeScript, this is how you can import `chai-spies`.
|
46 |
|
47 | ```
|
48 | import { expect } from 'chai';
|
49 | import spies from 'chai-spies';
|
50 | chai.use(spies);
|
51 | ```
|
52 |
|
53 |
|
54 | ## Building for the Browser
|
55 |
|
56 | Currently this package uses [rollup](https://rollupjs.org/) to bundle source code. Just use `npm run build` to build browser version.
|
57 |
|
58 | ## Chai Spies Api Reference
|
59 |
|
60 | ### Creating Spies
|
61 |
|
62 | In this module, a spy is either an empty function, or a wrapped named function.
|
63 | Once chai has been extended, you can create a spy through chai's own interface.
|
64 |
|
65 | ```js
|
66 | function original () {
|
67 | // do something cool
|
68 | }
|
69 |
|
70 | const spy = chai.spy(original);
|
71 |
|
72 | // then use in place of original
|
73 | ee.on('some event', spy);
|
74 |
|
75 | // or use without original
|
76 | const spyAgain = chai.spy();
|
77 | ee.on('some other event', spyAgain);
|
78 | ```
|
79 |
|
80 | #### spy.on
|
81 |
|
82 | `spy.on` allows to add spy on existing method of an object
|
83 |
|
84 | ```js
|
85 | const array = [1, 2, 3];
|
86 |
|
87 | chai.spy.on(array, 'push');
|
88 |
|
89 | // or multiple spies
|
90 | chai.spy.on(array, ['push', 'pop']);
|
91 | ```
|
92 |
|
93 | It's also possible to provide custom implementation of spied method:
|
94 |
|
95 | ```js
|
96 | chai.spy.on(array, 'push', function (...items) {
|
97 | // custom implementation of `push` method
|
98 | });
|
99 | ```
|
100 |
|
101 | Using arrow functions, it's also easy to replace method implementation with constant:
|
102 |
|
103 | ```js
|
104 | chai.spy.on(array, 'push', () => 5);
|
105 |
|
106 | // or more readable :)
|
107 | chai.spy.on(array, 'push', returns => 5);
|
108 | ```
|
109 |
|
110 | #### spy.interface
|
111 |
|
112 | This method creates a mock (or spy object): an interface with spies on each of the object's methods. The object's methods have either fake implementations or no implementation.
|
113 |
|
114 | ```js
|
115 | // with no implementation
|
116 | const arrayLike = chai.spy.interface('arrayLike', ['push', 'pop', 'filter']);
|
117 |
|
118 | // with fake implementation
|
119 | const arrayLike = chai.spy.interface({
|
120 | push(item) {
|
121 | this.__items = this.__items || [];
|
122 | return this.__items.push(item)
|
123 | },
|
124 | // other method implementations
|
125 | });
|
126 |
|
127 | arrayLike.push(5);
|
128 | ```
|
129 |
|
130 | #### spy.returns (Deprecated)
|
131 |
|
132 | `chai.spy.returns` is just a simple helper which creates a function that returns constant:
|
133 |
|
134 | ```js
|
135 | const returnTrue = chai.spy.returns(true);
|
136 |
|
137 | returnTrue(); // true
|
138 | ```
|
139 |
|
140 | Better to use arrow function:
|
141 |
|
142 | ```js
|
143 | const returnTrue = chai.spy(returns => true);
|
144 | ```
|
145 |
|
146 | ### Sandboxes
|
147 |
|
148 | Sandbox is a set of spies. Sandbox allows to track methods on objects and restore original methods with on `restore` call.
|
149 | To create sandbox:
|
150 |
|
151 | ```js
|
152 | const sandbox = chai.spy.sandbox();
|
153 |
|
154 | describe('Array', () => {
|
155 | let array;
|
156 |
|
157 | beforeEach(() => {
|
158 | array = [];
|
159 | sandbox.on(array, ['push', 'pop']);
|
160 | });
|
161 |
|
162 | afterEach(() => {
|
163 | sandbox.restore(); // restores original methods on `array`
|
164 | })
|
165 |
|
166 | it('allows to add items', () => {
|
167 | array.push(1);
|
168 |
|
169 | expect(array.push).to.have.been.called.with(1);
|
170 | });
|
171 | });
|
172 | ```
|
173 |
|
174 | `chai.spy.on` and `chai.spy.restore` are bound to default sandbox.
|
175 | So to restore all methods spied by `chai.spy.on`, just call `chai.spy.restore()` (without arguments).
|
176 |
|
177 | `restore` method accepts 2 optional arguments: object to restore and method or methods to restore. So, this calls are also valid:
|
178 |
|
179 | ```js
|
180 | const array = [1, 2, 3];
|
181 |
|
182 | chai.spy.on(array, ['push', 'pop']);
|
183 |
|
184 | chai.spy.restore(array) // restores all methods on object
|
185 | chai.spy.restore(array, 'push') // restores only `push` method
|
186 | ```
|
187 |
|
188 | ### Assertions
|
189 |
|
190 | #### .spy
|
191 |
|
192 | Asserts that object is a spy.
|
193 |
|
194 | ```js
|
195 | expect(spy).to.be.spy;
|
196 | spy.should.be.spy;
|
197 | ```
|
198 |
|
199 | #### .called
|
200 |
|
201 | Assert that a spy has been called. Negation passes through.
|
202 |
|
203 | ```js
|
204 | expect(spy).to.have.been.called();
|
205 | spy.should.have.been.called();
|
206 | ```
|
207 |
|
208 | Note that `called` can be used as a chainable method.
|
209 |
|
210 | #### .with
|
211 |
|
212 | Assert that a spy has been called with a given argument at least once,
|
213 | even if more arguments were provided.
|
214 |
|
215 | ```js
|
216 | spy('foo');
|
217 | expect(spy).to.have.been.called.with('foo');
|
218 | spy.should.have.been.called.with('foo');
|
219 | ```
|
220 |
|
221 | Will also pass for `spy('foo', 'bar')` and `spy(); spy('foo')`.
|
222 |
|
223 | If used with multiple arguments, assert that a spy has been called
|
224 | with all the given arguments at least once.
|
225 |
|
226 | ```js
|
227 | spy('foo', 'bar', 1);
|
228 | expect(spy).to.have.been.called.with('bar', 'foo');
|
229 | spy.should.have.been.called.with('bar', 'foo');
|
230 | ```
|
231 |
|
232 | #### .with.exactly
|
233 |
|
234 | Similar to .with, but will pass only if the list of arguments is
|
235 | exactly the same as the one provided.
|
236 |
|
237 | ```js
|
238 | spy();
|
239 | spy('foo', 'bar');
|
240 | expect(spy).to.have.been.called.with.exactly('foo', 'bar');
|
241 | spy.should.have.been.called.with.exactly('foo', 'bar');
|
242 | ```
|
243 |
|
244 | Will not pass for `spy('foo')`, `spy('bar')`, `spy('bar');
|
245 | spy('foo')`, `spy('foo'); spy('bar')`, `spy('bar', 'foo')` or
|
246 | `spy('foo', 'bar', 1)`.
|
247 |
|
248 | Can be used for calls with a single argument too.
|
249 |
|
250 | #### .always.with
|
251 |
|
252 | Assert that every time the spy has been called the argument list
|
253 | contained the given arguments.
|
254 |
|
255 | ```js
|
256 | spy('foo');
|
257 | spy('foo', 'bar');
|
258 | spy(1, 2, 'foo');
|
259 | expect(spy).to.have.been.called.always.with('foo');
|
260 | spy.should.have.been.called.always.with('foo');
|
261 | ```
|
262 |
|
263 | #### .always.with.exactly
|
264 |
|
265 | Assert that the spy has never been called with a different list of
|
266 | arguments than the one provided.
|
267 |
|
268 | ```js
|
269 | spy('foo');
|
270 | spy('foo');
|
271 | expect(spy).to.have.been.called.always.with.exactly('foo');
|
272 | spy.should.have.been.called.always.with.exactly('foo');
|
273 | ```
|
274 |
|
275 | #### .nth(n).called.with
|
276 |
|
277 | Asserts that the nth call of the spy has been made with the list of arguments provided. This assertion comes with other three flavors:
|
278 |
|
279 | * .first.called.with
|
280 | * .second.called.with
|
281 | * .third.called.with
|
282 |
|
283 | ```js
|
284 | spy('foo');
|
285 | spy('bar');
|
286 | spy('baz');
|
287 | spy('foobar');
|
288 | expect(spy).to.have.been.first.called.with('foo');
|
289 | spy.should.have.been.first.called.with('foo');
|
290 | expect(spy).on.nth(5).be.called.with('foobar');
|
291 | spy.should.on.nth(5).be.called.with('foobar');
|
292 | ```
|
293 |
|
294 | These assertions requires the spy to be called at least the
|
295 | number of times required, for example
|
296 |
|
297 | ```js
|
298 | spy('foo');
|
299 | spy('bar');
|
300 | expect(spy).to.have.been.third.called.with('baz');
|
301 | spy.should.have.been.third.called.with('baz');
|
302 | ```
|
303 |
|
304 | Won't pass because the spy has not been called a third time.
|
305 |
|
306 | #### .once
|
307 |
|
308 | Assert that a spy has been called exactly once.
|
309 |
|
310 | ```js
|
311 | expect(spy).to.have.been.called.once;
|
312 | expect(spy).to.not.have.been.called.once;
|
313 | spy.should.have.been.called.once;
|
314 | spy.should.not.have.been.called.once;
|
315 | ```
|
316 |
|
317 | #### .twice
|
318 |
|
319 | Assert that a spy has been called exactly twice.
|
320 |
|
321 | ```js
|
322 | expect(spy).to.have.been.called.twice;
|
323 | expect(spy).to.not.have.been.called.twice;
|
324 | spy.should.have.been.called.twice;
|
325 | spy.should.not.have.been.called.twice;
|
326 | ```
|
327 |
|
328 | #### .exactly(n)
|
329 |
|
330 | Assert that a spy has been called exactly `n` times.
|
331 |
|
332 | ```js
|
333 | expect(spy).to.have.been.called.exactly(3);
|
334 | expect(spy).to.not.have.been.called.exactly(3);
|
335 | spy.should.have.been.called.exactly(3);
|
336 | spy.should.not.have.been.called.exactly(3);
|
337 | ```
|
338 |
|
339 | #### .min(n) / .at.least(n)
|
340 |
|
341 | Assert that a spy has been called minimum of `n` times.
|
342 |
|
343 | ```js
|
344 | expect(spy).to.have.been.called.min(3);
|
345 | expect(spy).to.not.have.been.called.at.least(3);
|
346 | spy.should.have.been.called.at.least(3);
|
347 | spy.should.not.have.been.called.min(3);
|
348 | ```
|
349 |
|
350 | #### .max(n) / .at.most(n)
|
351 |
|
352 | Assert that a spy has been called maximum of `n` times.
|
353 |
|
354 | ```js
|
355 | expect(spy).to.have.been.called.max(3);
|
356 | expect(spy).to.not.have.been.called.at.most(3);
|
357 | spy.should.have.been.called.at.most(3);
|
358 | spy.should.not.have.been.called.max(3);
|
359 | ```
|
360 | #### .above(n) / .gt(n)
|
361 |
|
362 | Assert that a spy has been called more than `n` times.
|
363 |
|
364 | ```js
|
365 | expect(spy).to.have.been.called.above(3);
|
366 | expect(spy).to.not.have.been.called.gt(3);
|
367 | spy.should.have.been.called.gt(3);
|
368 | spy.should.not.have.been.called.above(3);
|
369 | ```
|
370 |
|
371 | #### .below(n) / .lt(n)
|
372 |
|
373 | Assert that a spy has been called fewer than `n` times.
|
374 |
|
375 | ```js
|
376 | expect(spy).to.have.been.called.below(3);
|
377 | expect(spy).to.not.have.been.called.lt(3);
|
378 | spy.should.have.been.called.lt(3);
|
379 | spy.should.not.have.been.called.below(3);
|
380 | ```
|
381 |
|
382 | ## Tests
|
383 |
|
384 | Tests are written using [mocha](http://github.com/visionmedia/mocha) in the BDD interface.
|
385 | Node tests can be executed using `npm test`. Browser tests can be seen by opening `test/browser/index.html`.
|
386 |
|
387 | ## Contributors
|
388 |
|
389 | project : chai-spies
|
390 | repo age : 3 years, 2 months
|
391 | active : 26 days
|
392 | commits : 77
|
393 | files : 12
|
394 | authors :
|
395 | 48 Jake Luer 62.3%
|
396 | 7 Glenn Jorde 9.1%
|
397 | 4 Keith Cirkel 5.2%
|
398 | 3 = 3.9%
|
399 | 3 Sergiy Stotskiy 3.9%
|
400 | 2 JamesMaroney 2.6%
|
401 | 2 PG Herveou 2.6%
|
402 | 2 Ryckes 2.6%
|
403 | 1 Veselin Todorov 1.3%
|
404 | 1 Steffen 1.3%
|
405 | 1 Daniel Walker 1.3%
|
406 | 1 Domenic Denicola 1.3%
|
407 | 1 Andre Jaenisch 1.3%
|
408 | 1 PG 1.3%
|
409 |
|
410 | ## License
|
411 |
|
412 | (The MIT License)
|
413 |
|
414 | Copyright (c) 2012 Jake Luer <jake@alogicalparadox.com>
|
415 |
|
416 | Permission is hereby granted, free of charge, to any person obtaining a copy
|
417 | of this software and associated documentation files (the "Software"), to deal
|
418 | in the Software without restriction, including without limitation the rights
|
419 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
420 | copies of the Software, and to permit persons to whom the Software is
|
421 | furnished to do so, subject to the following conditions:
|
422 |
|
423 | The above copyright notice and this permission notice shall be included in
|
424 | all copies or substantial portions of the Software.
|
425 |
|
426 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
427 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
428 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
429 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
430 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
431 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
432 | THE SOFTWARE.
|