UNPKG

20.9 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### Arbitrary data
168
169- `arb.bless({...}): arbitrary a`
170
171 Bless generator, shrink, show triple with with `.smap` property.
172
173- `.smap(f: a -> b, g: b -> a, newShow: (b -> string)?): arbitrary b`
174
175 Transform `arbitrary a` into `arbitrary b`. For example:
176
177 `g` should be a [right inverse](http://en.wikipedia.org/wiki/Surjective_function#Surjections_as_right_invertible_functions) of `f`.
178
179 ```js
180 positiveIntegersArb = nat.smap(
181 function (x) { return x + 1; },
182 function (x) { return x - 1; });
183 ```
184
185### Primitive arbitraries
186
187- `integer: arbitrary integer`
188- `integer(maxsize: nat): arbitrary integer`
189- `integer(minsize: integer, maxsize: integer): arbitrary integer`
190
191 Integers, ℤ
192
193- `nat: arbitrary nat`
194- `nat(maxsize: nat): arbitrary nat`
195
196 Natural numbers, ℕ (0, 1, 2...)
197
198- `number: arbitrary number`
199- `number(maxsize: number): arbitrary number`
200- `number(min: number, max: number): arbitrary number`
201
202 JavaScript numbers, "doubles", ℝ. `NaN` and `Infinity` are not included.
203
204- `uint8: arbitrary nat`
205- `uint16: arbitrary nat`
206- `uint32: arbitrary nat`
207
208- `int8: arbitrary integer`
209- `int16: arbitrary integer`
210- `int32: arbitrary integer`
211
212- `bool: arbitrary bool`
213
214 Booleans, `true` or `false`.
215
216- `datetime: arbitrary datetime`
217
218 Random datetime
219
220- `elements(args: array a): arbitrary a`
221
222 Random element of `args` array.
223
224- `char: arbitrary char`
225
226 Single character
227
228- `asciichar: arbitrary char`
229
230 Single ascii character (0x20-0x7e inclusive, no DEL)
231
232- `string: arbitrary string`
233
234- `notEmptyString: arbitrary string`
235
236 Generates strings which are not empty.
237
238- `asciistring: arbitrary string`
239
240- `json: arbitrary json`
241
242 JavaScript Objects: boolean, number, string, array of `json` values or object with `json` values.
243
244- `value: arbitrary json`
245
246- `falsy: arbitrary *`
247
248 Generates falsy values: `false`, `null`, `undefined`, `""`, `0`, and `NaN`.
249
250- `constant(x: a): arbitrary a`
251
252 Returns an unshrinkable arbitrary that yields the given object.
253
254### Arbitrary combinators
255
256- `nonshrink(arb: arbitrary a): arbitrary a`
257
258 Non shrinkable version of arbitrary `arb`.
259
260- `array(arb: arbitrary a): arbitrary (array a)`
261
262- `nearray(arb: arbitrary a): arbitrary (array a)`
263
264- `unit: arbitrary ()`
265
266- `either(arbA: arbitrary a, arbB : arbitrary b): arbitrary (either a b)`
267
268- `pair(arbA: arbitrary a, arbB : arbitrary b): arbitrary (pair a b)`
269
270 If not specified `a` and `b` are equal to `value()`.
271
272- `tuple(arbs: (arbitrary a, arbitrary b...)): arbitrary (a, b...)`
273
274- `map(arb: arbitrary a): arbitrary (map a)`
275
276 Generates a JavaScript object with properties of type `A`.
277
278- `oneof(gs : array (arbitrary a)...) : arbitrary a`
279
280 Randomly uses one of the given arbitraries.
281
282- `record(spec: { key: arbitrary a... }): arbitrary { key: a... }`
283
284 Generates a javascript object with given record spec.
285
286- `fn(arb: arbitrary a): arbitrary (b -> a)`
287- `fun(arb: arbitrary a): arbitrary (b -> a)`
288
289### Generator functions
290
291A generator function, `generator a`, is a function `(size: nat) -> a`, which generates a value of given size.
292
293Generator combinators are auto-curried:
294
295```js
296var xs = generator.array(shrink.nat, 1); // ≡
297var ys = generator.array(shrink.nat)(1);
298```
299
300In purely functional approach `generator a` would be explicitly stateful computation:
301`(size: nat, rng: randomstate) -> (a, randomstate)`.
302*JSVerify* uses an implicit random number generator state,
303but the value generation is deterministic (tests reproduceable),
304if the primitives from *random* module are used.
305
306- `generator.bless(f: nat -> a): generator a`
307
308 Bless function with `.map` and `.flatmap` properties.
309
310- `.map(f: a -> b): generator b`
311
312 Map `generator a` into `generator b`. For example:
313
314 ```js
315 positiveIntegersGenerator = nat.generator.map(
316 function (x) { return x + 1; });
317 ```
318
319- `.isomap(f: a -> generator b): generator b`
320
321 Monadic bind for generators.
322
323- `generator.constant(x: a): generator a`
324
325- `generator.combine(gen: generator a..., f: a... -> b): generator b`
326
327- `generator.oneof(gens: list (generator a)): generator a`
328
329- `generator.recursive(genZ: generator a, genS: generator a -> generator a): generator a`
330
331- `generator.pair(genA: generator a, genB: generator b): generator (a, b)`
332
333- `generator.either(genA: generator a, genB: generator b): generator (either a b)`
334
335- `generator.unit: generator ()
336
337 `unit` is an empty tuple, i.e. empty array in JavaScript representation. This is useful as a building block.
338
339- `generator.tuple(gens: (generator a, generator b...): generator (a, b...)`
340
341- `generator.array(gen: generator a): generator (array a)`
342
343- `generator.nearray(gen: generator a): generator (array a)`
344
345- `generator.char: generator char`
346
347- `generator.string: generator string`
348
349- `generator.nestring: generator string`
350
351- `generator.asciichar: generator char`
352
353- `generator.asciistring: generator string`
354
355- `generator.map(gen: generator a): generator (map a)`
356
357- `generator.json: generator json`
358
359### Shrink functions
360
361A shrink function, `shrink a`, is a function `a -> [a]`, returning an array of *smaller* values.
362
363Shrink combinators are auto-curried:
364
365```js
366var xs = shrink.array(shrink.nat, [1]); // ≡
367var ys = shrink.array(shrink.nat)([1]);
368```
369
370- `shrink.bless(f: a -> [a]): shrink a`
371
372 Bless function with `.isomap` property.
373
374- `.isomap(f: a -> b, g: b -> a): shrink b`
375
376 Transform `shrink a` into `shrink b`. For example:
377
378 ```js
379 positiveIntegersShrink = nat.shrink.isomap(
380 function (x) { return x + 1; },
381 function (x) { return x - 1; });
382 ```
383
384- `shrink.noop: shrink a`
385
386- `shrink.pair(shrA: shrink a, shrB: shrink b): shrink (a, b)`
387
388- `shrink.either(shrA: shrink a, shrB: shrink b): shrink (either a b)`
389
390- `shrink.tuple(shrs: (shrink a, shrink b...)): shrink (a, b...)`
391
392- `shrink.array(shr: shrink a): shrink (array a)`
393
394- `shrink.nearray(shr: shrink a): shrink (nearray a)`
395
396- `shrink.record(shrs: { key: shrink a... }): shrink { key: a... }`
397
398### Show functions
399
400- `show.def(x : a): string`
401
402 Currently implemented as `JSON.stringify`.
403
404- `show.pair(showA: a -> string, showB: b -> string, x: (a, b)): string`
405
406- `show.either(showA: a -> string, showB: b -> string, e: either a b): string`
407
408- `show.tuple(shrinks: (a -> string, b -> string...), x: (a, b...)): string`
409
410- `show.array(shrink: a -> string, x: array a): string`
411
412### Random functions
413
414- `random(min: int, max: int): int`
415
416 Returns random int from `[min, max]` range inclusively.
417
418 ```js
419 getRandomInt(2, 3) // either 2 or 3
420 ```
421
422- `random.number(min: number, max: number): number`
423
424 Returns random number from `[min, max)` range.
425
426### either
427
428- `either.left(value: a): either a b`
429
430- `either.right(value: b): either a b`
431
432- `either.either(l: a -> x, r: b -> x): x`
433
434- `either.isEqual(other: either a b): bool
435
436 TODO: add `eq` optional parameter
437
438- `either.bimap(f: a -> c, g: b -> d): either c d`
439
440 ```js
441 either.bimap(compose(f, g), compose(h, i)) ≡ either.bimap(g, i).bimap(f, h);
442 ```
443
444- `either.first(f: a -> c): either c b`
445
446 ```js
447 either.first(f) ≡ either.bimap(f, utils.identity)
448 ```
449
450- `either.second(g: b -> d): either a d`
451
452 ```js
453 either.second(g) === either.bimap(utils.identity, g)
454 ```
455
456### Utility functions
457
458Utility functions are exposed (and documented) only to make contributions to jsverify more easy.
459The changes here don't follow semver, i.e. there might be backward-incompatible changes even in patch releases.
460
461Use [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.
462
463- `utils.isEqual(x: json, y: json): bool`
464
465 Equality test for `json` objects.
466
467- `utils.force(x: a | () -> a) : a`
468
469 Evaluate `x` as nullary function, if it is one.
470
471- `utils.merge(x: obj, y: obj): obj`
472
473 Merge two objects, a bit like `_.extend({}, x, y)`.
474
475## Contributing
476
477- `README.md` is generated from the source with [ljs](https://github.com/phadej/ljs)
478- `jsverify.standalone.js` is also generated by the build process
479- Before creating a pull request run `make test`, yet travis will do it for you.
480
481## Release History
482
483- **0.5.3** &mdash; *2015-04-21* &mdash; More algebra
484 - `unit` and `either` arbitraries
485 - `arbitrary.smap` to help creating compound data
486- **0.5.2** &mdash; *2015-04-10* &mdash; `show.def` -change
487- **0.5.1** &mdash; *2015-02-19* &mdash; Dependencies bump
488 - We also work on 0.12 and iojs!
489- **0.5.0** &mdash; *2014-12-24* &mdash; Merry Chrismas 2014!
490 - Documention cleanup
491- **0.5.0-beta.2** &mdash; *2014-12-21* &mdash; Beta 2!
492 - Pair &amp; tuple related code cleanup
493 - Update `CONTRIBUTING.md`
494 - Small documentation type fixes
495 - Bless `jsc.elements` shrink
496- **0.5.0-beta.1** &mdash; *2014-12-20* &mdash; Beta!
497 - `bless` don't close over (uses `this`)
498 - Cleanup generator module
499 - Other code cleanup here and there
500- **0.4.6** &mdash; *2014-11-30* &mdash; better shrinks &amp; recursive
501 - Implemented shrinks: [#51](https://github.com/jsverify/jsverify/issues/51)
502 - `jsc.generator.recursive`: [#37](https://github.com/jsverify/jsverify/issues/37)
503 - array, nearray &amp; map generators return a bit smaller results (*log2* of size)
504- **0.4.5** &mdash; *2014-11-22* &mdash; stuff
505 - `generator.combine` &amp; `.flatmap`
506 - `nat`, `integer`, `number` &amp; and `string` act as objects too
507- **0.4.4** &mdash; *2014-11-22* &mdash; new generators
508 - New generators: `nearray`, `nestring`
509 - `generator.constant`
510 - zero-ary `jsc.property` (it ∘ assert)
511 - `jsc.sampler`
512- **0.4.3** &mdash; *2014-11-08* &mdash; jsc.property
513 - Now you can write your bdd specs without any boilerplate
514 - support for nat-litearls in dsl [#36](https://github.com/jsverify/jsverify/issues/36)
515 ```js
516 describe("Math.abs", function () {
517 jsc.property("result is non-negative", "integer 100", function (x) {
518 return Math.abs(x) >= 0;
519 });
520 });
521 ```
522 - Falsy generator [#42](https://github.com/jsverify/jsverify/issues/42)
523- **0.4.2** &mdash; *2014-11-03* &mdash; User environments for DSL
524 - User environments for DSL
525 - Generator prototype `map`, and shrink prototype `isomap`
526 - JSON generator works with larger sizes
527- **0.4.1** Move to own organization in GitHub
528- **0.4.0** &mdash; *2014-10-27* &mdash; typify-dsl &amp; more arbitraries.
529 Changes from **0.3.6**:
530 - DSL for `forall` and `suchthat`
531 - new primitive arbitraries
532 - `oneof` behaves as in QuickCheck (BREAKING CHANGE)
533 - `elements` is new name of old `oneof`
534 - Other smaller stuff under the hood
535- **0.4.0**-beta.4 generator.oneof
536- **0.4.0**-beta.3 Expose shrink and show modules
537- **0.4.0**-beta.2 Move everything around
538 - Better looking README.md!
539- **0.4.0**-beta.1 Beta!
540 - Dev Dependencies update
541- **0.4.0**-alpha8 oneof &amp; record -dsl support
542 - also `jsc.compile`
543 - record is shrinkable!
544- **0.4.0**-alpha7 oneof &amp; record
545 - *oneof* and *record* generator combinators ([@fson](https://github.com/fson))
546 - Fixed uint\* generators
547 - Default test size increased to 10
548 - Numeric generators with size specified are independent of test size ([#20](https://github.com/phadej/jsverify/issues/20))
549- **0.4.0**-alpha6 more primitives
550 - int8, int16, int32, uint8, uint16, uint32
551 - char, asciichar and asciistring
552 - value &rarr; json
553 - use eslint
554- **0.4.0**-alpha5 move david to be devDependency
555- **0.4.0**-alpha4 more typify
556 - `suchchat` supports typify dsl
557 - `oneof` &rarr; `elements` to be in line with QuickCheck
558 - Added versions of examples using typify dsl
559- **0.4.0**-alpha3 David, npm-freeze and jscs
560- **0.4.0**-alpha2 Fix typo in readme
561- **0.4.0**-alpha1 typify
562 - DSL for `forall`
563 ```js
564 var bool_fn_applied_thrice = jsc.forall("bool -> bool", "bool", check);
565 ```
566
567 - generator arguments, which are functions are evaluated. One can now write:
568 ```js
569 jsc.forall(jsc.nat, check) // previously had to be jsc.nat()
570 ```
571
572- **0.3.6** map generator
573- **0.3.5** Fix forgotten rngState in console output
574- **0.3.4** Dependencies update
575- **0.3.3** Dependencies update
576- **0.3.2** `fun` &rarr; `fn`
577- **0.3.1** Documentation typo fixes
578- **0.3.0** Major changes
579 - random generate state handling
580 - `--jsverifyRngState` parameter value used when run on node
581 - karma tests
582 - use make
583 - dependencies update
584- **0.2.0** Use browserify
585- **0.1.4** Mocha test suite
586 - major cleanup
587- **0.1.3** gen.show and exception catching
588- **0.1.2** Added jsc.assert
589- **0.1.1** Use grunt-literate
590- **0.1.0** Usable library
591- **0.0.2** Documented preview
592- **0.0.1** Initial preview
593
594## Related work
595
596### JavaScript
597
598- [JSCheck](http://www.jscheck.org/)
599- [claire](https://npmjs.org/package/claire)
600- [gent](https://npmjs.org/package/gent)
601- [fatcheck](https://npmjs.org/package/fatcheck)
602- [quickcheck](https://npmjs.org/package/quickcheck)
603- [qc.js](https://bitbucket.org/darrint/qc.js/)
604- [quick\_check](https://www.npmjs.org/package/quick_check)
605- [gencheck](https://github.com/graue/gentest)
606- [node-quickcheck](https://github.com/mcandre/node-quickcheck)
607
608### Others
609
610- [Wikipedia - QuickCheck](http://en.wikipedia.org/wiki/QuickCheck)
611- [Haskell - QuickCheck](http://hackage.haskell.org/package/QuickCheck) [Introduction](http://www.haskell.org/haskellwiki/Introduction_to_QuickCheck1)
612- [Erlang - QuviQ](http://www.quviq.com/index.html)
613- [Erlang - triq](https://github.com/krestenkrab/triq)
614- [Scala - ScalaCheck](https://github.com/rickynils/scalacheck)
615- [Clojure - test.check](https://github.com/clojure/test.check)
616- [Python - Hypothesis](https://github.com/DRMacIver/hypothesis)
617
618The MIT License (MIT)
619
620Copyright (c) 2013, 2014 Oleg Grenrus
621
622Permission is hereby granted, free of charge, to any person obtaining a copy
623of this software and associated documentation files (the "Software"), to deal
624in the Software without restriction, including without limitation the rights
625to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
626copies of the Software, and to permit persons to whom the Software is
627furnished to do so, subject to the following conditions:
628
629The above copyright notice and this permission notice shall be included in
630all copies or substantial portions of the Software.
631
632THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
633IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
634FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
635AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
636LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
637OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
638THE SOFTWARE.