UNPKG

19.1 kBMarkdownView Raw
1# JSVerify
2
3<img src="https://raw.githubusercontent.com/jsverify/jsverify/master/jsverify-300.png" align="right" height="100" />
4
5> Property based checking. Like QuickCheck.
6
7[![Build Status](https://secure.travis-ci.org/jsverify/jsverify.svg?branch=master)](http://travis-ci.org/jsverify/jsverify)
8[![NPM version](https://badge.fury.io/js/jsverify.svg)](http://badge.fury.io/js/jsverify)
9[![Dependency Status](https://david-dm.org/jsverify/jsverify.svg)](https://david-dm.org/jsverify/jsverify)
10[![devDependency Status](https://david-dm.org/jsverify/jsverify/dev-status.svg)](https://david-dm.org/jsverify/jsverify#info=devDependencies)
11[![Code Climate](https://img.shields.io/codeclimate/github/jsverify/jsverify.svg)](https://codeclimate.com/github/jsverify/jsverify)
12
13## Getting Started
14
15Install the module with: `npm install jsverify`
16
17## Synopsis
18
19```js
20var jsc = require("jsverify");
21
22// forall (f : bool -> bool) (b : bool), f (f (f b)) = f(b).
23var boolFnAppliedThrice =
24 jsc.forall("bool -> bool", "bool", function (f, b) {
25 return f(f(f(b))) === f(b);
26 });
27
28jsc.assert(boolFnAppliedThrice);
29// OK, passed 100 tests
30```
31
32## Documentation
33
34### Usage with [mocha](http://visionmedia.github.io/mocha/)
35
36Using jsverify with mocha is easy, just define the properties and use `jsverify.assert`.
37
38Starting from version 0.4.3 you can write your specs without any boilerplate:
39
40```js
41describe("sort", function () {
42 jsc.property("idempotent", "array nat", function (arr) {
43 return _.isEqual(sort(sort(arr)), sort(arr));
44 });
45});
46```
47
48You can also provide `--jsverifyRngState state` command line argument, to run tests with particular random generator state.
49
50```
51$ mocha examples/nat.js
52
531) natural numbers are less than 90:
54 Error: Failed after 49 tests and 1 shrinks. rngState: 074e9b5f037a8c21d6; Counterexample: 90;
55
56$ mocha examples/nat.js --grep 'are less than' --jsverifyRngState 074e9b5f037a8c21d6
57
581) natural numbers are less than 90:
59 Error: Failed after 1 tests and 1 shrinks. rngState: 074e9b5f037a8c21d6; Counterexample: 90;
60```
61
62Errorneous case is found with first try.
63
64### Usage with [jasmine](http://pivotal.github.io/jasmine/)
65
66Check [jasmineHelpers.js](helpers/jasmineHelpers.js) and [jasmineHelpers2.js](helpers/jasmineHelpers2.js) for jasmine 1.3 and 2.0 respectively.
67
68## API
69
70> _Testing shows the presence, not the absence of bugs._
71>
72> Edsger W. Dijkstra
73
74To show that propositions hold, we need to construct proofs.
75There are two extremes: proof by example (unit tests) and formal (machine-checked) proof.
76Property-based testing is somewhere in between.
77We formulate propositions, invariants or other properties we believe to hold, but
78only test it to hold for numerous (randomly generated) values.
79
80Types and function signatures are written in [Coq](http://coq.inria.fr/)/[Haskell](http://www.haskell.org/haskellwiki/Haskell) influented style:
81C# -style `List<T> filter(List<T> v, Func<T, bool> predicate)` is represented by
82`filter (v : array T) (predicate : T -> bool) : array T` in our style.
83
84`jsverify` can operate with both synchronous and asynchronous-promise properties.
85Generally every property can be wrapped inside [functor](http://learnyouahaskell.com/functors-applicative-functors-and-monoids),
86for now in either identity or promise functor, for synchronous and promise properties respectively.
87
88### Properties
89
90- `forall(arbs: arbitrary a ..., userenv: (map arbitrary)?, prop : a -> property): property`
91
92 Property constructor
93
94- `check (prop: property, opts: checkoptions?): result`
95
96 Run random checks for given `prop`. If `prop` is promise based, result is also wrapped in promise.
97
98 Options:
99 - `opts.tests` - test count to run, default 100
100 - `opts.size` - maximum size of generated values, default 5
101
102 - `opts.quiet` - do not `console.log`
103 - `opts.rngState` - state string for the rng
104
105- `assert(prop: property, opts: checkoptions?) : void`
106
107 Same as `check`, but throw exception if property doesn't hold.
108
109- `property(name: string, ...)`
110
111 Assuming there is globally defined `it`, the same as:
112
113 ```js
114 it(name, function () {
115 jsc.assert(jsc.forall(...));
116 }
117 ```
118
119 You can use `property` to write facts too:
120 ```js
121 jsc.property("+0 === -0", function () {
122 return +0 === -0;
123 });
124 ```
125
126- `sampler(arb: arbitrary a, genSize: nat = 10): (sampleSize: nat?) -> a`
127
128 Create a sampler for a given arbitrary with an optional size. Handy when used in
129 a REPL:
130 ```
131 > jsc = require('jsverify') // or require('./lib/jsverify') w/in the project
132 ...
133 > jsonSampler = jsc.sampler(jsc.json, 4)
134 [Function]
135 > jsonSampler()
136 0.08467432763427496
137 > jsonSampler()
138 [ [ [] ] ]
139 > jsonSampler()
140 ''
141 > sampledJson(2)
142 [-0.4199344692751765, false]
143 ```
144
145### Types
146
147- `generator a` is a function `(size: nat) -> a`.
148- `show` is a function `a -> string`.
149- `shrink` is a function `a -> [a]`, returning *smaller* values.
150- `arbitrary a` is a triple of generator, shrink and show functions.
151 - `{ generator: nat -> a, shrink : a -> array a, show: a -> string }`
152
153### DSL for input parameters
154
155There is a small DSL to help with `forall`. For example the two definitions below are equivalent:
156```js
157var bool_fn_applied_thrice = jsc.forall("bool -> bool", "bool", check);
158var bool_fn_applied_thrice = jsc.forall(jsc.fn(jsc.bool()), jsc.bool(), check);
159```
160
161The DSL is based on a subset of language recognized by [typify-parser](https://github.com/phadej/typify-parser):
162- *identifiers* are fetched from the predefined environment.
163- *applications* are applied as one could expect: `"array bool"` is evaluated to `jsc.array(jsc.bool)`.
164- *functions* are supported: `"bool -> bool"` is evaluated to `jsc.fn(jsc.bool())`.
165- *square brackets* are treated as a shorthand for the array type: `"[nat]"` is evaluated to `jsc.array(jsc.nat)`.
166
167### Primitive arbitraries
168
169- `integer: arbitrary integer`
170- `integer(maxsize: nat): arbitrary integer`
171- `integer(minsize: integer, maxsize: integer): arbitrary integer`
172
173 Integers, ℤ
174
175- `nat: arbitrary nat`
176- `nat(maxsize: nat): arbitrary nat`
177
178 Natural numbers, ℕ (0, 1, 2...)
179
180- `number: arbitrary number`
181- `number(maxsize: number): arbitrary number`
182- `number(min: number, max: number): arbitrary number`
183
184 JavaScript numbers, "doubles", ℝ. `NaN` and `Infinity` are not included.
185
186- `uint8: arbitrary nat`
187- `uint16: arbitrary nat`
188- `uint32: arbitrary nat`
189
190- `int8: arbitrary integer`
191- `int16: arbitrary integer`
192- `int32: arbitrary integer`
193
194- `bool: arbitrary bool`
195
196 Booleans, `true` or `false`.
197
198- `datetime: arbitrary datetime`
199
200 Random datetime
201
202- `elements(args: array a): arbitrary a`
203
204 Random element of `args` array.
205
206- `char: arbitrary char`
207
208 Single character
209
210- `asciichar: arbitrary char`
211
212 Single ascii character (0x20-0x7e inclusive, no DEL)
213
214- `string: arbitrary string`
215
216- `notEmptyString: arbitrary string`
217
218 Generates strings which are not empty.
219
220- `asciistring: arbitrary string`
221
222- `json: arbitrary json`
223
224 JavaScript Objects: boolean, number, string, array of `json` values or object with `json` values.
225
226- `value: arbitrary json`
227
228- `falsy: arbitrary *`
229
230 Generates falsy values: `false`, `null`, `undefined`, `""`, `0`, and `NaN`.
231
232- `constant(x: a): arbitrary a`
233
234 Returns an unshrinkable arbitrary that yields the given object.
235
236### Arbitrary combinators
237
238- `nonshrink(arb: arbitrary a): arbitrary a`
239
240 Non shrinkable version of arbitrary `arb`.
241
242- `array(arb: arbitrary a): arbitrary (array a)`
243
244- `nearray(arb: arbitrary a): arbitrary (array a)`
245
246- `pair(arbA: arbitrary a, arbB : arbitrary b): arbitrary (pair a b)`
247
248 If not specified `a` and `b` are equal to `value()`.
249
250- `tuple(arbs: (arbitrary a, arbitrary b...)): arbitrary (a, b...)`
251
252- `map(arb: arbitrary a): arbitrary (map a)`
253
254 Generates a JavaScript object with properties of type `A`.
255
256- `oneof(gs : array (arbitrary a)...) : arbitrary a`
257
258 Randomly uses one of the given arbitraries.
259
260- `record(spec: { key: arbitrary a... }): arbitrary { key: a... }`
261
262 Generates a javascript object with given record spec.
263
264- `fn(arb: arbitrary a): arbitrary (b -> a)`
265- `fun(arb: arbitrary a): arbitrary (b -> a)`
266
267### Generator functions
268
269A generator function, `generator a`, is a function `(size: nat) -> a`, which generates a value of given size.
270
271Generator combinators are auto-curried:
272
273```js
274var xs = generator.array(shrink.nat, 1); // ≡
275var ys = generator.array(shrink.nat)(1);
276```
277
278In purely functional approach `generator a` would be explicitly stateful computation:
279`(size: nat, rng: randomstate) -> (a, randomstate)`.
280*JSVerify* uses an implicit random number generator state,
281but the value generation is deterministic (tests reproduceable),
282if the primitives from *random* module are used.
283
284- `generator.bless(f: nat -> a): generator a`
285
286 Bless function with `.map` and `.flatmap` properties.
287
288- `.map(f: a -> b): generator b`
289
290 Map `generator a` into `generator b`. For example:
291
292 ```js
293 positiveIntegersGenerator = nat.generator.map(
294 function (x) { return x + 1; });
295 ```
296
297- `.isomap(f: a -> generator b): generator b`
298
299 Monadic bind for generators.
300
301- `generator.constant(x: a): generator a`
302
303- `generator.combine(gen: generator a..., f: a... -> b): generator b`
304
305- `generator.oneof(gens: list (generator a)): generator a`
306
307- `generator.recursive(genZ: generator a, genS: generator a -> generator a): generator a`
308
309- `generator.pair(genA: generator a, genB: generator b): generator (a, b)`
310
311- `generator.tuple(gens: (generator a, generator b...): generator (a, b...)`
312
313- `generator.array(gen: generator a): generator (array a)`
314
315- `generator.nearray(gen: generator a): generator (array a)`
316
317- `generator.char: generator char`
318
319- `generator.string: generator string`
320
321- `generator.nestring: generator string`
322
323- `generator.asciichar: generator char`
324
325- `generator.asciistring: generator string`
326
327- `generator.map(gen: generator a): generator (map a)`
328
329- `generator.json: generator json`
330
331### Shrink functions
332
333A shrink function, `shrink a`, is a function `a -> [a]`, returning an array of *smaller* values.
334
335Shrink combinators are auto-curried:
336
337```js
338var xs = shrink.array(shrink.nat, [1]); // ≡
339var ys = shrink.array(shrink.nat)([1]);
340```
341
342- `shrink.bless(f: a -> [a]): shrink a`
343
344 Bless function with `.isomap` property.
345
346- `.isomap(f: a -> b, g: b -> a): shrink b`
347
348 Transform `shrink a` into `shrink b`. For example:
349
350 ```js
351 positiveIntegersShrink = nat.shrink.isomap(
352 function (x) { return x + 1; },
353 function (x) { return x - 1; });
354 ```
355
356- `shrink.noop: shrink a`
357
358- `shrink.pair(shrA: shrink a, shrB: shrink b): shrink (a, b)`
359
360- `shrink.tuple(shrs: (shrink a, shrink b...)): shrink (a, b...)`
361
362- `shrink.array(shr: shrink a): shrink (array a)`
363
364- `shrink.nearray(shr: shrink a): shrink (nearray a)`
365
366- `shrink.record(shrs: { key: shrink a... }): shrink { key: a... }`
367
368### Show functions
369
370- `show.def(x : a): string`
371
372 Currently implemented as `JSON.stringify`.
373
374- `show.pair(showA: a -> string, showB: b -> string, x: (a, b)): string`
375
376- `show.tuple(shrinks: (a -> string, b -> string...), x: (a, b...)): string`
377
378- `show.array(shrink: a -> string, x: array a): string`
379
380### Random functions
381
382- `random(min: int, max: int): int`
383
384 Returns random int from `[min, max]` range inclusively.
385
386 ```js
387 getRandomInt(2, 3) // either 2 or 3
388 ```
389
390- `random.number(min: number, max: number): number`
391
392 Returns random number from `[min, max)` range.
393
394### Utility functions
395
396Utility functions are exposed (and documented) only to make contributions to jsverify more easy.
397The changes here don't follow semver, i.e. there might be backward-incompatible changes even in patch releases.
398
399Use [underscore.js](http://underscorejs.org/), [lodash](https://lodash.com/), [ramda](http://ramda.github.io/ramdocs/docs/), [lazy.js](http://danieltao.com/lazy.js/) or some other utility belt.
400
401- `utils.isEqual(x: json, y: json): bool`
402
403 Equality test for `json` objects.
404
405- `utils.force(x: a | () -> a) : a`
406
407 Evaluate `x` as nullary function, if it is one.
408
409- `utils.merge(x: obj, y: obj): obj`
410
411 Merge two objects, a bit like `_.extend({}, x, y)`.
412
413## Contributing
414
415- `README.md` is generated from the source with [ljs](https://github.com/phadej/ljs)
416- `jsverify.standalone.js` is also generated by the build process
417- Before creating a pull request run `make test`, yet travis will do it for you.
418
419## Release History
420
421- **0.5.2** &mdash; *2015-04-10* &mdash; `show.def` -change
422- **0.5.1** &mdash; *2015-02-19* &mdash; Dependencies bump
423 - We also work on 0.12 and iojs!
424- **0.5.0** &mdash; *2014-12-24* &mdash; Merry Chrismas 2014!
425 - Documention cleanup
426- **0.5.0-beta.2** &mdash; *2014-12-21* &mdash; Beta 2!
427 - Pair &amp; tuple related code cleanup
428 - Update `CONTRIBUTING.md`
429 - Small documentation type fixes
430 - Bless `jsc.elements` shrink
431- **0.5.0-beta.1** &mdash; *2014-12-20* &mdash; Beta!
432 - `bless` don't close over (uses `this`)
433 - Cleanup generator module
434 - Other code cleanup here and there
435- **0.4.6** &mdash; *2014-11-30* &mdash; better shrinks &amp; recursive
436 - Implemented shrinks: [#51](https://github.com/jsverify/jsverify/issues/51)
437 - `jsc.generator.recursive`: [#37](https://github.com/jsverify/jsverify/issues/37)
438 - array, nearray &amp; map generators return a bit smaller results (*log2* of size)
439- **0.4.5** &mdash; *2014-11-22* &mdash; stuff
440 - `generator.combine` &amp; `.flatmap`
441 - `nat`, `integer`, `number` &amp; and `string` act as objects too
442- **0.4.4** &mdash; *2014-11-22* &mdash; new generators
443 - New generators: `nearray`, `nestring`
444 - `generator.constant`
445 - zero-ary `jsc.property` (it ∘ assert)
446 - `jsc.sampler`
447- **0.4.3** &mdash; *2014-11-08* &mdash; jsc.property
448 - Now you can write your bdd specs without any boilerplate
449 - support for nat-litearls in dsl [#36](https://github.com/jsverify/jsverify/issues/36)
450 ```js
451 describe("Math.abs", function () {
452 jsc.property("result is non-negative", "integer 100", function (x) {
453 return Math.abs(x) >= 0;
454 });
455 });
456 ```
457 - Falsy generator [#42](https://github.com/jsverify/jsverify/issues/42)
458- **0.4.2** &mdash; *2014-11-03* &mdash; User environments for DSL
459 - User environments for DSL
460 - Generator prototype `map`, and shrink prototype `isomap`
461 - JSON generator works with larger sizes
462- **0.4.1** Move to own organization in GitHub
463- **0.4.0** &mdash; *2014-10-27* &mdash; typify-dsl &amp; more arbitraries.
464 Changes from **0.3.6**:
465 - DSL for `forall` and `suchthat`
466 - new primitive arbitraries
467 - `oneof` behaves as in QuickCheck (BREAKING CHANGE)
468 - `elements` is new name of old `oneof`
469 - Other smaller stuff under the hood
470- **0.4.0**-beta.4 generator.oneof
471- **0.4.0**-beta.3 Expose shrink and show modules
472- **0.4.0**-beta.2 Move everything around
473 - Better looking README.md!
474- **0.4.0**-beta.1 Beta!
475 - Dev Dependencies update
476- **0.4.0**-alpha8 oneof &amp; record -dsl support
477 - also `jsc.compile`
478 - record is shrinkable!
479- **0.4.0**-alpha7 oneof &amp; record
480 - *oneof* and *record* generator combinators ([@fson](https://github.com/fson))
481 - Fixed uint\* generators
482 - Default test size increased to 10
483 - Numeric generators with size specified are independent of test size ([#20](https://github.com/phadej/jsverify/issues/20))
484- **0.4.0**-alpha6 more primitives
485 - int8, int16, int32, uint8, uint16, uint32
486 - char, asciichar and asciistring
487 - value &rarr; json
488 - use eslint
489- **0.4.0**-alpha5 move david to be devDependency
490- **0.4.0**-alpha4 more typify
491 - `suchchat` supports typify dsl
492 - `oneof` &rarr; `elements` to be in line with QuickCheck
493 - Added versions of examples using typify dsl
494- **0.4.0**-alpha3 David, npm-freeze and jscs
495- **0.4.0**-alpha2 Fix typo in readme
496- **0.4.0**-alpha1 typify
497 - DSL for `forall`
498 ```js
499 var bool_fn_applied_thrice = jsc.forall("bool -> bool", "bool", check);
500 ```
501
502 - generator arguments, which are functions are evaluated. One can now write:
503 ```js
504 jsc.forall(jsc.nat, check) // previously had to be jsc.nat()
505 ```
506
507- **0.3.6** map generator
508- **0.3.5** Fix forgotten rngState in console output
509- **0.3.4** Dependencies update
510- **0.3.3** Dependencies update
511- **0.3.2** `fun` &rarr; `fn`
512- **0.3.1** Documentation typo fixes
513- **0.3.0** Major changes
514 - random generate state handling
515 - `--jsverifyRngState` parameter value used when run on node
516 - karma tests
517 - use make
518 - dependencies update
519- **0.2.0** Use browserify
520- **0.1.4** Mocha test suite
521 - major cleanup
522- **0.1.3** gen.show and exception catching
523- **0.1.2** Added jsc.assert
524- **0.1.1** Use grunt-literate
525- **0.1.0** Usable library
526- **0.0.2** Documented preview
527- **0.0.1** Initial preview
528
529## Related work
530
531### JavaScript
532
533- [JSCheck](http://www.jscheck.org/)
534- [claire](https://npmjs.org/package/claire)
535- [gent](https://npmjs.org/package/gent)
536- [fatcheck](https://npmjs.org/package/fatcheck)
537- [quickcheck](https://npmjs.org/package/quickcheck)
538- [qc.js](https://bitbucket.org/darrint/qc.js/)
539- [quick\_check](https://www.npmjs.org/package/quick_check)
540- [gencheck](https://github.com/graue/gentest)
541- [node-quickcheck](https://github.com/mcandre/node-quickcheck)
542
543### Others
544
545- [Wikipedia - QuickCheck](http://en.wikipedia.org/wiki/QuickCheck)
546- [Haskell - QuickCheck](http://hackage.haskell.org/package/QuickCheck) [Introduction](http://www.haskell.org/haskellwiki/Introduction_to_QuickCheck1)
547- [Erlang - QuviQ](http://www.quviq.com/index.html)
548- [Erlang - triq](https://github.com/krestenkrab/triq)
549- [Scala - ScalaCheck](https://github.com/rickynils/scalacheck)
550- [Clojure - test.check](https://github.com/clojure/test.check)
551- [Python - Hypothesis](https://github.com/DRMacIver/hypothesis)
552
553The MIT License (MIT)
554
555Copyright (c) 2013, 2014 Oleg Grenrus
556
557Permission is hereby granted, free of charge, to any person obtaining a copy
558of this software and associated documentation files (the "Software"), to deal
559in the Software without restriction, including without limitation the rights
560to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
561copies of the Software, and to permit persons to whom the Software is
562furnished to do so, subject to the following conditions:
563
564The above copyright notice and this permission notice shall be included in
565all copies or substantial portions of the Software.
566
567THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
568IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
569FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
570AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
571LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
572OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
573THE SOFTWARE.