1 | # schema-shot
|
2 |
|
3 | > Framework-agnostic snapshot testing using "schema by example" for highly dynamic data
|
4 |
|
5 | [![NPM][npm-icon] ][npm-url]
|
6 |
|
7 | [![Build status][ci-image] ][ci-url]
|
8 | [![semantic-release][semantic-image] ][semantic-url]
|
9 | [![js-standard-style][standard-image]][standard-url]
|
10 |
|
11 | If you like [snap-shot][snap-shot] (snapshot testing for any JS framework),
|
12 | but have data that is hard to pin down, maybe this package will be useful.
|
13 | Instead of storing *literal* data snapshot, it stores [json-schema][json-schema]
|
14 | derived from a the snapshot object seen **first time** (using
|
15 | [validate-by-example][validate-by-example] to derive it). Next time an object
|
16 | arrives, it will be validated against the *schema*. Any missing property,
|
17 | or new one will trigger an exception.
|
18 |
|
19 | ## Example
|
20 |
|
21 | Imagine we are fetching most popular item from an API service. Obviously
|
22 | it changes often, so we cannot just store it as a snapshot for direct
|
23 | comparison. We could massage the data and derive
|
24 | [invariant snapshots][snapshot testing], but that is boilerplate code!
|
25 |
|
26 | Instead, use `schema-shot`!
|
27 |
|
28 | ```sh
|
29 | npm install --save-dev schema-shot
|
30 | ```
|
31 |
|
32 | In your test
|
33 |
|
34 | ```js
|
35 | // spec.js
|
36 | const schemaShot = require('schema-shot')
|
37 | it('returns most popular item', () => {
|
38 | const top = api.getMostPopularItem()
|
39 | schemaShot(top)
|
40 | })
|
41 | ```
|
42 |
|
43 | Suppose first time it runs, the API returns `top = {id: '45a12e'}` - an object
|
44 | with just its `id` property. The `__snapshots__/spec.js.schema-shot` file
|
45 | will be saved with
|
46 |
|
47 | ```js
|
48 | exports['returns most popular item 1'] = {
|
49 | "$schema": "http://json-schema.org/draft-04/schema#",
|
50 | "type": "object",
|
51 | "properties": {
|
52 | "id": {
|
53 | "type": "string",
|
54 | "required": true
|
55 | }
|
56 | },
|
57 | "additionalProperties": false
|
58 | }
|
59 | ```
|
60 |
|
61 | Now imagine the same day later running again. The API returns something else,
|
62 | but the object still has same *shape*, just a different id `{id: 8812f0}`.
|
63 | This object passes the schema validation step.
|
64 |
|
65 | A week later, the new API version gets deployed. Now it returns the top
|
66 | item, but instead of `id` property, it returns `uuid` property. The test
|
67 | will fail!
|
68 |
|
69 | ```sh
|
70 | $ npm test
|
71 | Error: schema difference
|
72 | data has additional properties
|
73 | data.id: is required
|
74 | ```
|
75 |
|
76 | [json-schema]: http://json-schema.org/
|
77 | [validate-by-example]: https://github.com/bahmutov/validate-by-example
|
78 | [snapshot testing]: https://glebbahmutov.com/blog/snapshot-testing/
|
79 |
|
80 | ## Difference with snapshot testing
|
81 |
|
82 | The schema shots do not store the direct information. A good example is
|
83 | a test using [snap-shot][snap-shot] and [fake-todos][fake-todos] in
|
84 | [test/todos-spec.js](test/todos-spec.js). The snapshot test always fails
|
85 | on the second run, because a returned todo is *different*. The JSON
|
86 | schema on the other hand stays consistent, as long as *fake-todos*
|
87 | returns objects with same shape.
|
88 |
|
89 | ```js
|
90 | const snapshot = require('snap-shot')
|
91 | const schemaShot = require('schema-shot')
|
92 | const generate = require('fake-todos')
|
93 | describe('fake-todos', () => {
|
94 | it.skip('returns a different todo', () => {
|
95 | const todos = generate(1)
|
96 | snapshot(todos[0])
|
97 | /*
|
98 | Fails of course, because every todo is different
|
99 | 1) fake-todos returns a different todo:
|
100 | Error: snapshot difference
|
101 | {
|
102 | id: "4e040570-..." => "129a55b4-..."
|
103 | what: "do adults" => "skip chess"
|
104 | }
|
105 | */
|
106 | })
|
107 | it('returns a todo', () => {
|
108 | const todos = generate(1)
|
109 | schemaShot(todos[0])
|
110 | })
|
111 | })
|
112 | ```
|
113 |
|
114 | [snap-shot]: https://github.com/bahmutov/snap-shot
|
115 | [fake-todos]: https://github.com/bahmutov/fake-todos
|
116 |
|
117 | ### Small print
|
118 |
|
119 | Author: Gleb Bahmutov <gleb.bahmutov@gmail.com> © 2017
|
120 |
|
121 | * [@bahmutov](https://twitter.com/bahmutov)
|
122 | * [glebbahmutov.com](http://glebbahmutov.com)
|
123 | * [blog](http://glebbahmutov.com/blog)
|
124 |
|
125 | License: MIT - do anything with the code, but don't blame me if it does not work.
|
126 |
|
127 | Support: if you find any problems with this module, email / tweet /
|
128 | [open issue](https://github.com/bahmutov/schema-shot/issues) on Github
|
129 |
|
130 | ## MIT License
|
131 |
|
132 | Copyright (c) 2017 Gleb Bahmutov <gleb.bahmutov@gmail.com>
|
133 |
|
134 | Permission is hereby granted, free of charge, to any person
|
135 | obtaining a copy of this software and associated documentation
|
136 | files (the "Software"), to deal in the Software without
|
137 | restriction, including without limitation the rights to use,
|
138 | copy, modify, merge, publish, distribute, sublicense, and/or sell
|
139 | copies of the Software, and to permit persons to whom the
|
140 | Software is furnished to do so, subject to the following
|
141 | conditions:
|
142 |
|
143 | The above copyright notice and this permission notice shall be
|
144 | included in all copies or substantial portions of the Software.
|
145 |
|
146 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
147 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
148 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
149 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
150 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
151 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
152 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
153 | OTHER DEALINGS IN THE SOFTWARE.
|
154 |
|
155 | [npm-icon]: https://nodei.co/npm/schema-shot.svg?downloads=true
|
156 | [npm-url]: https://npmjs.org/package/schema-shot
|
157 | [ci-image]: https://travis-ci.org/bahmutov/schema-shot.svg?branch=master
|
158 | [ci-url]: https://travis-ci.org/bahmutov/schema-shot
|
159 | [semantic-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
|
160 | [semantic-url]: https://github.com/semantic-release/semantic-release
|
161 | [standard-image]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg
|
162 | [standard-url]: http://standardjs.com/
|