1 | [![TODO board](https://imdone.io/api/1.0/projects/5b1adecc1883d42a1fbf805d/badge)](https://imdone.io/app#/board/bahmutov/snap-shot-it)
|
2 |
|
3 | # snap-shot-it
|
4 |
|
5 | > Smarter snapshot utility for Mocha and BDD test runners + data-driven testing!
|
6 |
|
7 | [![NPM][npm-icon] ][npm-url]
|
8 |
|
9 | [![Build status][ci-image] ][ci-url]
|
10 | [![semantic-release][semantic-image] ][semantic-url]
|
11 | [![js-standard-style][standard-image]][standard-url]
|
12 |
|
13 | ## Why
|
14 |
|
15 | This tool makes [snapshot testing][snapshot testing] for Mocha (and other BDD)
|
16 | frameworks quick and painless. This module spies on global `it` function,
|
17 | which allows it to accurately get test information (beating static code parsing
|
18 | done in [snap-shot][snap-shot]); it should work in transpiled code.
|
19 |
|
20 | [snapshot testing]: https://glebbahmutov.com/blog/snapshot-testing/
|
21 |
|
22 | This package uses [snap-shot-compare](https://github.com/bahmutov/snap-shot-compare)
|
23 | to display object and text difference intelligently.
|
24 |
|
25 | This function also includes [data-driven][data-driven] testing mode,
|
26 | similar to [sazerac][sazerac], see [Data-driven testing](#data-driven-testing)
|
27 | section below.
|
28 |
|
29 | [data-driven]: https://hackernoon.com/sazerac-data-driven-testing-for-javascript-e3408ac29d8c#.9s4ikt67d
|
30 | [sazerac]: https://github.com/mikec/sazerac
|
31 |
|
32 | ## Install
|
33 |
|
34 | Requires [Node](https://nodejs.org/en/) version 4 or above.
|
35 |
|
36 | ```sh
|
37 | npm install --save-dev snap-shot-it
|
38 | ```
|
39 |
|
40 | ## Use
|
41 |
|
42 | Example from [spec.js](src/spec.js)
|
43 |
|
44 | ```js
|
45 | const snapshot = require('snap-shot-it')
|
46 | describe('example', () => {
|
47 | it('works', () => {
|
48 | snapshot(add(10, 20))
|
49 | snapshot('a text message')
|
50 | return Promise.resolve(42).then(snapshot)
|
51 | })
|
52 | })
|
53 | ```
|
54 |
|
55 | Run Mocha tests, then open the created
|
56 | [__snapshots__/spec.js](__snapshots__/spec.js) file
|
57 |
|
58 | ```js
|
59 | exports['example works 1'] = 30
|
60 |
|
61 | exports['example works 2'] = "a text message"
|
62 |
|
63 | exports['example works 3'] = 42
|
64 | ```
|
65 |
|
66 | Suppose you change the resolved value from `42` to `80`
|
67 |
|
68 | ```js
|
69 | const snapshot = require('snap-shot-it')
|
70 | describe('example', () => {
|
71 | it('works', () => {
|
72 | snapshot(add(10, 20))
|
73 | snapshot('a text message')
|
74 | return Promise.resolve(80).then(snapshot)
|
75 | })
|
76 | })
|
77 | ```
|
78 |
|
79 | The test will fail
|
80 |
|
81 | ```
|
82 | 1) example works:
|
83 | Error: 42 !== 80
|
84 | ```
|
85 |
|
86 | The error message should intelligently handle numbers, objects, arrays,
|
87 | multi-line text, etc.
|
88 |
|
89 | ## Advanced use
|
90 |
|
91 | You can see the saves snapshot values by running with environment variable
|
92 |
|
93 | ```bash
|
94 | SNAPSHOT_SHOW=1 npm test
|
95 | ```
|
96 |
|
97 | You can see snapshot values without writing them into the snapshot file
|
98 |
|
99 | ```bash
|
100 | SNAPSHOT_DRY=1 npm test
|
101 | ```
|
102 |
|
103 | You can update snapshot values
|
104 |
|
105 | ```bash
|
106 | SNAPSHOT_UPDATE=1 npm test
|
107 | ```
|
108 |
|
109 | By default, the snapshots are saved sorted alphabetically. You can skip sorting using an environment variable
|
110 |
|
111 | ```bash
|
112 | SNAPSHOT_SKIP_SORTING=1 npm test
|
113 | ```
|
114 |
|
115 | ## Named snapshots
|
116 |
|
117 | Renaming tests might lead to confusion and pruning snapshots. You can name the snapshots
|
118 | yourself
|
119 |
|
120 | ```js
|
121 | const value = 42
|
122 | snapshot('my name', value)
|
123 | ```
|
124 |
|
125 | The snapshots will be saved as
|
126 |
|
127 | ```js
|
128 | exports['my name'] = 42
|
129 | ```
|
130 |
|
131 | **Note** you should make sure that the name is unique per spec file.
|
132 |
|
133 | ## Pruning
|
134 |
|
135 | If the test run is successful and executed _all_ tests (there was no `.only`) then snapshots without a test are pruned.
|
136 |
|
137 | ## Data-driven testing
|
138 |
|
139 | Writing multiple input / output pairs for a function under test quickly
|
140 | becomes tedious. Luckily, you can test a function by providing multiple
|
141 | inputs and a single snapshot of function's *behavior* will be saved.
|
142 |
|
143 | ```js
|
144 | // checks if n is prime
|
145 | const isPrime = n => ...
|
146 | it('tests prime', () => {
|
147 | snapshot(isPrime, 1, 2, 3, 4, 5, 6, 7, 8, 9)
|
148 | })
|
149 | ```
|
150 |
|
151 | The saved snapshot file will have clear mapping between given input and
|
152 | produced result
|
153 |
|
154 | ```js
|
155 | // snapshot file
|
156 | exports['tests prime 1'] = {
|
157 | "name": "isPrime",
|
158 | "behavior": [
|
159 | {
|
160 | "given": 1,
|
161 | "expect": false
|
162 | },
|
163 | {
|
164 | "given": 2,
|
165 | "expect": true
|
166 | },
|
167 | {
|
168 | "given": 3,
|
169 | "expect": true
|
170 | },
|
171 | {
|
172 | "given": 4,
|
173 | "expect": false
|
174 | },
|
175 | {
|
176 | "given": 5,
|
177 | "expect": true
|
178 | },
|
179 | ...
|
180 | ]
|
181 | }
|
182 | ```
|
183 |
|
184 | You can also test functions that expect multiple arguments by providing
|
185 | arrays of inputs.
|
186 |
|
187 | ```js
|
188 | const add = (a, b) => a + b
|
189 | it('checks behavior of binary function add', () => {
|
190 | snapshot(add, [1, 2], [2, 2], [-5, 5], [10, 11])
|
191 | })
|
192 | ```
|
193 |
|
194 | Again, the snapshot file gives clear picture of the `add` behavior
|
195 |
|
196 | ```js
|
197 | // snapshot file
|
198 | exports['checks behavior of binary function add 1'] = {
|
199 | "name": "add",
|
200 | "behavior": [
|
201 | {
|
202 | "given": [
|
203 | 1,
|
204 | 2
|
205 | ],
|
206 | "expect": 3
|
207 | },
|
208 | {
|
209 | "given": [
|
210 | 2,
|
211 | 2
|
212 | ],
|
213 | "expect": 4
|
214 | },
|
215 | {
|
216 | "given": [
|
217 | -5,
|
218 | 5
|
219 | ],
|
220 | "expect": 0
|
221 | },
|
222 | {
|
223 | "given": [
|
224 | 10,
|
225 | 11
|
226 | ],
|
227 | "expect": 21
|
228 | }
|
229 | ]
|
230 | }
|
231 | ```
|
232 |
|
233 | See [src/data-driven-spec.js](src/data-driven-spec.js) for more examples.
|
234 |
|
235 | ## Debugging
|
236 |
|
237 | Run with environment variable `DEBUG=snap-shot-it ...` to see log messages.
|
238 | Because under the hood it uses [snap-shot-core][snap-shot-core] you might
|
239 | want to show messages from both libraries with `DEBUG=snap-shot* ...`
|
240 |
|
241 | ## Examples
|
242 |
|
243 | ### TypeScript
|
244 |
|
245 | An example using [ts-mocha](https://github.com/piotrwitek/ts-mocha) is
|
246 | shown in folder [ts-demo](ts-demo)
|
247 |
|
248 | ### CoffeeScript
|
249 |
|
250 | CoffeeScript example is in [coffee-demo](coffee-demo) folder. Watch mode is
|
251 | working properly.
|
252 |
|
253 | ## Inspiration
|
254 |
|
255 | Came during WorkBar Cambridge Happy Hour on the terrace as I was thinking about
|
256 | difficulty of adding CoffeeScript / TypeScript support to
|
257 | [snap-shot][snap-shot] project. Got the idea of overriding `global.it` when
|
258 | loading `snap-shot` because a day before I wrote [repeat-it][repeat-it]
|
259 | which overrides it and it is very simple [repeat/src/index.js][repeat source].
|
260 |
|
261 | [snap-shot]: https://github.com/bahmutov/snap-shot
|
262 | [repeat-it]: https://github.com/bahmutov/repeat-it
|
263 | [repeat source]: https://github.com/bahmutov/repeat-it/blob/master/src/index.js
|
264 |
|
265 | ## Related projects
|
266 |
|
267 | This NPM module is part of my experiments with snapshot testing. There are
|
268 | lots of other ones, blog posts and slides on this topic.
|
269 |
|
270 | * [snap-shot-core][snap-shot-core] implements loading and saving snapshots
|
271 | * [snap-shot](https://github.com/bahmutov/snap-shot) is an alternative to this
|
272 | package that tries to determine spec name using stack trace and static
|
273 | source code parsing. Hard to do for transpiled code!
|
274 | * [schema-shot](https://github.com/bahmutov/schema-shot) is
|
275 | "schema by example" snapshot testing
|
276 | * [subset-shot](https://github.com/bahmutov/subset-shot)
|
277 | where new value can be a superset of the saved snapshot
|
278 | * Blog post [Picking snapshot library](https://glebbahmutov.com/blog/picking-snapshot-library/)
|
279 | * Slides [Snapshot testing the hard way](https://slides.com/bahmutov/snapshot-testing-the-hard-way)
|
280 |
|
281 | [snap-shot-core]: https://github.com/bahmutov/snap-shot-core
|
282 |
|
283 | ### Small print
|
284 |
|
285 | Author: Gleb Bahmutov <gleb.bahmutov@gmail.com> © 2017
|
286 |
|
287 | * [@bahmutov](https://twitter.com/bahmutov)
|
288 | * [glebbahmutov.com](https://glebbahmutov.com)
|
289 | * [blog](https://glebbahmutov.com/blog)
|
290 |
|
291 | License: MIT - do anything with the code, but don't blame me if it does not work.
|
292 |
|
293 | Support: if you find any problems with this module, email / tweet /
|
294 | [open issue](https://github.com/bahmutov/snap-shot-it/issues) on Github
|
295 |
|
296 | ## MIT License
|
297 |
|
298 | Copyright (c) 2017 Gleb Bahmutov <gleb.bahmutov@gmail.com>
|
299 |
|
300 | Permission is hereby granted, free of charge, to any person
|
301 | obtaining a copy of this software and associated documentation
|
302 | files (the "Software"), to deal in the Software without
|
303 | restriction, including without limitation the rights to use,
|
304 | copy, modify, merge, publish, distribute, sublicense, and/or sell
|
305 | copies of the Software, and to permit persons to whom the
|
306 | Software is furnished to do so, subject to the following
|
307 | conditions:
|
308 |
|
309 | The above copyright notice and this permission notice shall be
|
310 | included in all copies or substantial portions of the Software.
|
311 |
|
312 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
313 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
314 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
315 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
316 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
317 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
318 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
319 | OTHER DEALINGS IN THE SOFTWARE.
|
320 |
|
321 | [npm-icon]: https://nodei.co/npm/snap-shot-it.svg?downloads=true
|
322 | [npm-url]: https://npmjs.org/package/snap-shot-it
|
323 | [ci-image]: https://travis-ci.org/bahmutov/snap-shot-it.svg?branch=master
|
324 | [ci-url]: https://travis-ci.org/bahmutov/snap-shot-it
|
325 | [semantic-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
|
326 | [semantic-url]: https://github.com/semantic-release/semantic-release
|
327 | [standard-image]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg
|
328 | [standard-url]: http://standardjs.com/
|