property-based testing for godly tests
TRANSCRIPT
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce
!
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce
!
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 3
! Gabe from Vancouver
🐙 @garbles
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce
careers.unbounce.com
4
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce
Property-Based Testing for Godly Tests
5
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 6
Why do we write tests?
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 7
"Program testing can be a very effective way to show the presence of bugs, but it is hopelessly inadequate for showing their absence."
- Edsger Dijkstra
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 8
Tests prevent regressions!
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 9
Tests prevent regressions!(that you know about)
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 10
Example-Based Tests
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 11
function sort(arr: number[]): number[]
it('does nothing to empty arrays', () !=> { expect(sort([])).toEqual([]); });
it('sorts unsorted arrays', () !=> { expect(sort([6, 2, 1])).toEqual([1, 2, 6]); });
it('does not change a sorted array', () !=> { expect(sort([1, 2, 3, 4])).toEqual([1, 2, 3, 4]); });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 12
function sort(arr: number[]): number[]
it('does nothing to empty arrays', () !=> { expect(sort([])).toEqual([]); });
it('sorts unsorted arrays', () !=> { expect(sort([6, 2, 1])).toEqual([1, 2, 6]); });
it('does not change a sorted array', () !=> { expect(sort([1, 2, 3, 4])).toEqual([1, 2, 3, 4]); });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 13
function sort(arr: number[]): number[]
it('does nothing to empty arrays', () !=> { expect(sort([])).toEqual([]); });
it('sorts unsorted arrays', () !=> { expect(sort([6, 2, 1])).toEqual([1, 2, 6]); });
it('does not change a sorted array', () !=> { expect(sort([1, 2, 3, 4])).toEqual([1, 2, 3, 4]); });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 14
function sort(arr: number[]): number[]
it('does nothing to empty arrays', () !=> { expect(sort([])).toEqual([]); });
it('sorts unsorted arrays', () !=> { expect(sort([6, 2, 1])).toEqual([1, 2, 6]); });
it('does not change a sorted array', () !=> { expect(sort([1, 2, 3, 4])).toEqual([1, 2, 3, 4]); });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 15
Set of all possible inputs
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 16
Fuzz Tests
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 17
it('does not throw', () !=> { const arr = arrayOfRandomNumbers(); expect(() !=> sort(arr)).not.toThrow(); });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 18
Set of all possible inputs
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 19
it('sorts unsorted arrays', () !=> { const arr = arrayOfRandomNumbers(); const sorted = !// ?????? expect(sort(arr)).toEqual(sorted); });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 20
Property-Based Tests
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 21
it('keeps the same length', () !=> { const arr = arrayOfRandomNumbers(); expect(sort(arr).length).toEqual(arr.length); });
it('is idempotent', () !=> { const arr = arrayOfRandomNumbers(); expect(sort(sort(arr))).toEqual(sort(arr)); });
it('every member is less than or equal to the next one', () !=> { const arr = arrayOfRandomNumbers(); const sorted = sort(arr);
for (let i in sorted) { const current = sorted[i]; const next = sorted[i + 1];
if (next !!!== undefined) { expect(current).toBeLessThanOrEqual(next); } } });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 22
it('keeps the same length', () !=> { const arr = arrayOfRandomNumbers(); expect(sort(arr).length).toEqual(arr.length); });
it('is idempotent', () !=> { const arr = arrayOfRandomNumbers(); expect(sort(sort(arr))).toEqual(sort(arr)); });
it('every member is less than or equal to the next one', () !=> { const arr = arrayOfRandomNumbers(); const sorted = sort(arr);
for (let i in sorted) { const current = sorted[i]; const next = sorted[i + 1];
if (next !!!== undefined) { expect(current).toBeLessThanOrEqual(next); } } });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 23
it('keeps the same length', () !=> { const arr = arrayOfRandomNumbers(); expect(sort(arr).length).toEqual(arr.length); });
it('is idempotent', () !=> { const arr = arrayOfRandomNumbers(); expect(sort(sort(arr))).toEqual(sort(arr)); });
it('every member is less than or equal to the next one', () !=> { const arr = arrayOfRandomNumbers(); const sorted = sort(arr);
for (let i in sorted) { const current = sorted[i]; const next = sorted[i + 1];
if (next !!!== undefined) { expect(current).toBeLessThanOrEqual(next); } } });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 24
Set of all possible inputs
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 25
"Example-Based tests are very narrow in their input and broad in their expectations."
- Jessica Kerr
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 26
Quickcheck
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 27
Testcheck Testcheck.js
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 28
Deliberate randomness
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 29
arrayOfRandomNumbers(); !// [1, 500, 6012], [45, 12, 579, 77, -8]!!...
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 30
Generators
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 31
import { gen } from 'testcheck';
gen.string; !// 'a1x', '', 'e4r'!!...
gen.int;
gen.bool;
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 32
import { gen } from 'testcheck';
gen.string;
gen.int; !// 0, -1, 3, 1!!...
gen.bool;
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 33
import { gen } from 'testcheck';
gen.string;
gen.int;
gen.bool; !// true, true, false!!...
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 34
import { gen } from 'testcheck';
const personGen = gen.object({ name: gen.alphaNumString, age: gen.posInt, }); !// {name: 'er54', age: 2}!!...
gen.array(gen.int);
const peopleGen = gen.array(personGen);
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 35
import { gen } from 'testcheck';
const personGen = gen.object({ name: gen.alphaNumString, age: gen.posInt, });
gen.array(gen.int); !// [3], []!!...
const peopleGen = gen.array(personGen);
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 36
import { gen } from 'testcheck';
const personGen = gen.object({ name: gen.alphaNumString, age: gen.posInt, });
gen.array(gen.int);
const peopleGen = gen.array(personGen); !// [{name: 'e', age: 1}]!!...
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 37
import { sample, gen } from 'testcheck';
sample(gen.array(gen.int), 20);
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 38
[ [], [], [2], [-1], [], [5, 0], [-6, -4, -5], [-2, -7, -6], [6, 7, -8, 7], [], [4, 2, 7], [-5, -2], [-10, -7, 0, 5], [8, -7, 3, 5, 11, -4, 5, -1, -6, -12, 9], [-6, 0, 4, 9, -2, 3, -4, -6, -6, 5, 3, 3], [-13, -11, -6, -13], [-16, 5, 0, 8, 16], [-6, -17, 14], [0, 13, -18, -15, -1, 0, 12, 1, 4, -15, 16, 5, -2, 6, -3], [7, 19, -1, 1, 12, 18], ];
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 39
import { sample, gen } from 'testcheck';
const personGen = gen.object({ name: gen.alphaNumString, age: gen.posInt, });
sample(personGen, 20);
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 40
[ { name: '', age: 0 }, { name: 'D', age: 1 }, { name: 'nn', age: 0 }, { name: 'r8', age: 3 }, { name: 'el1H', age: 0 }, { name: 't04', age: 2 }, { name: '47A15bF', age: 3 }, { name: 'N', age: 2 }, { name: 'DF18GqP', age: 7 }, { name: 'iKuySuF4', age: 5 }, { name: 'bU6', age: 0 }, { name: '1Zd', age: 11 }, { name: 'dR62wPY6K4jo', age: 5 }, { name: 'c57Rv3y', age: 5 }, !// !!... ];
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 41
it('is idempotent', () !=> { const arr = arrayOfRandomNumbers(); expect(sort(sort(arr))).toEqual(sort(arr)); });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 42
import { gen } from 'testcheck';
check.it('is idempotent', gen.array(gen.int), arr !=> { expect(sort(sort(arr))).toEqual(sort(arr)); });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 43
import { gen } from 'testcheck';
check.it('is idempotent', gen.array(gen.int), arr !=> { expect(sort(sort(arr))).toEqual(sort(arr)); });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 44
import { gen } from 'testcheck';
check.it('keeps the same length', gen.array(gen.int), arr !=> { expect(sort(arr).length).toEqual(arr.length); });
check.it('is idempotent', gen.array(gen.int), arr !=> { expect(sort(sort(arr))).toEqual(sort(arr)); });
check.it('every member is less than or equal to the next one', gen.array(gen.int), arr !=> { const sorted = sort(arr);
for (let i in sorted) { const current = sorted[i]; const next = sorted[i + 1];
if (next !!!== undefined) { expect(current).toBeLessThanOrEqual(next); } } });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 45
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 46
Shrinking
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 47
{ result: false, 'failing-size': 2, 'num-tests': 3, fail: [-12, 1, -12, 4], shrunk: { 'total-nodes-visited': 2, depth: 1, result: false, smallest: [-2, -2] } };
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 48
Super useful!
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 49
Where does it fall apart in JavaScript?
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 50
type Person = { name: string, age: number, };
function savePerson(person: Person): Person
import { gen } from 'testcheck';
const personGen = gen.object({ name: gen.alphaNumString, age: gen.posInt, });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 51
type Person = { name: string, age: number, };
function savePerson(person: Person): Person
import { gen } from 'testcheck';
const personGen = gen.object({ name: gen.alphaNumString, age: gen.posInt, });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 52
type Person = { name: string, age: number, };
function savePerson(person: Person): Person
import { gen } from 'testcheck';
const personGen = gen.object({ name: gen.alphaNumString, age: gen.posInt, });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 53
type ActivationRule = { id: string, schemaVersion: 10, ubCode: string, embUuid: string, trigger: Trigger, meta: RuleMeta, urlTargets: UrlTargets, displaySettings: DisplaySettings, published: PublishedState, frequency: Frequency, cookieTargets: CookieTargets, geoTargets: GeoTargets, referrerTargets: ReferrerTargets, event: Event, version: string, parentVersion!?: string, dimensions: Dimensions, integrations: Integrations };
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 54
type ActivationRule = { id: string, schemaVersion: 10, ubCode: string, embUuid: string, trigger: Trigger, meta: RuleMeta, urlTargets: UrlTargets, displaySettings: DisplaySettings, published: PublishedState, frequency: Frequency, cookieTargets: CookieTargets, geoTargets: GeoTargets, referrerTargets: ReferrerTargets, event: Event, version: string, parentVersion!?: string, dimensions: Dimensions, integrations: Integrations };
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 55
type ActivationRule = { id: string, schemaVersion: 10, ubCode: string, embUuid: string, trigger: Trigger, meta: RuleMeta, urlTargets: UrlTargets, displaySettings: DisplaySettings, published: PublishedState, frequency: Frequency, cookieTargets: CookieTargets, geoTargets: GeoTargets, referrerTargets: ReferrerTargets, event: Event, version: string, parentVersion!?: string, dimensions: Dimensions, integrations: Integrations };
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 56
type TriggerName = 'exit' | 'welcome' | 'timed' | 'scroll' | 'scrollUp' | 'click';
type $Trigger<N: TriggerName, PN, PV> = { name: $Subtype<N>, parameters: [{ name: $Subtype<PN>, value: $Subtype<PV> }] };
type Trigger = $Trigger<'exit', 'exit' | 'topMargin', '20px'> | $Trigger<'welcome', 'delay', '0'> | $Trigger<'timed', 'delay', string> | $Trigger<'scroll', 'scrollPercent', string> | $Trigger<'scrollUp', 'scrollUp', ''> | $Trigger<'click', 'clickId' | 'clickClass' | 'clickSelector', string>;
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 57
type TriggerName = 'exit' | 'welcome' | 'timed' | 'scroll' | 'scrollUp' | 'click';
type $Trigger<N: TriggerName, PN, PV> = { name: $Subtype<N>, parameters: [{ name: $Subtype<PN>, value: $Subtype<PV> }] };
type Trigger = $Trigger<'exit', 'exit' | 'topMargin', '20px'> | $Trigger<'welcome', 'delay', '0'> | $Trigger<'timed', 'delay', string> | $Trigger<'scroll', 'scrollPercent', string> | $Trigger<'scrollUp', 'scrollUp', ''> | $Trigger<'click', 'clickId' | 'clickClass' | 'clickSelector', string>;
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 58
type TriggerName = 'exit' | 'welcome' | 'timed' | 'scroll' | 'scrollUp' | 'click';
type $Trigger<N: TriggerName, PN, PV> = { name: $Subtype<N>, parameters: [{ name: $Subtype<PN>, value: $Subtype<PV> }] };
type Trigger = $Trigger<'exit', 'exit' | 'topMargin', '20px'> | $Trigger<'welcome', 'delay', '0'> | $Trigger<'timed', 'delay', string> | $Trigger<'scroll', 'scrollPercent', string> | $Trigger<'scrollUp', 'scrollUp', ''> | $Trigger<'click', 'clickId' | 'clickClass' | 'clickSelector', string>;
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 59
type TriggerName = 'exit' | 'welcome' | 'timed' | 'scroll' | 'scrollUp' | 'click';
type $Trigger<N: TriggerName, PN, PV> = { name: $Subtype<N>, parameters: [{ name: $Subtype<PN>, value: $Subtype<PV> }] };
type Trigger = $Trigger<'exit', 'exit' | 'topMargin', '20px'> | $Trigger<'welcome', 'delay', '0'> | $Trigger<'timed', 'delay', string> | $Trigger<'scroll', 'scrollPercent', string> | $Trigger<'scrollUp', 'scrollUp', ''> | $Trigger<'click', 'clickId' | 'clickClass' | 'clickSelector', string>;
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 60
import type {ActivationRule as ActivationRuleV0} from './v00'; import type {ActivationRule as ActivationRuleV1} from './v01'; import type {ActivationRule as ActivationRuleV2} from './v02'; import type {ActivationRule as ActivationRuleV3} from './v03'; import type {ActivationRule as ActivationRuleV4} from './v04'; import type {ActivationRule as ActivationRuleV5} from './v05'; import type {ActivationRule as ActivationRuleV6} from './v06'; import type {ActivationRule as ActivationRuleV7} from './v07'; import type {ActivationRule as ActivationRuleV8} from './v08'; import type {ActivationRule as ActivationRuleV9} from './v09'; import type {ActivationRule as ActivationRuleV10} from './v10';
type AnyActivationRule = ActivationRuleV10 | ActivationRuleV9 | ActivationRuleV8 | ActivationRuleV7 | ActivationRuleV6 | ActivationRuleV5 | ActivationRuleV4 | ActivationRuleV3 | ActivationRuleV2 | ActivationRuleV1 | ActivationRuleV0;
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce
const upgradeActivationRule = (version: number, rule: AnyActivationRule): AnyActivationRule
61
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 62
it('is backwards compatible', () !=> { const activationRule = createActivationRule(); const { schemaVersion } = activationRule;
const upgradedRule = upgradeActivationRule( MAX_SCHEMA_VERSION, activationRule );
const originalRule = upgradeActivationRule( schemaVersion, upgradedRule );
expect(originalRule).toEqual(activationRule); });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 63
it('is backwards compatible', () !=> { const activationRule = createActivationRule(); const { schemaVersion } = activationRule;
const upgradedRule = upgradeActivationRule( MAX_SCHEMA_VERSION, activationRule );
const originalRule = upgradeActivationRule( schemaVersion, upgradedRule );
expect(originalRule).toEqual(activationRule); });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 64
it('is backwards compatible', () !=> { const activationRule = createActivationRule(); const { schemaVersion } = activationRule;
const upgradedRule = upgradeActivationRule( MAX_SCHEMA_VERSION, activationRule );
const originalRule = upgradeActivationRule( schemaVersion, upgradedRule );
expect(originalRule).toEqual(activationRule); });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 65
it('is backwards compatible', () !=> { const activationRule = createActivationRule(); const { schemaVersion } = activationRule;
const upgradedRule = upgradeActivationRule( MAX_SCHEMA_VERSION, activationRule );
const originalRule = upgradeActivationRule( schemaVersion, upgradedRule );
expect(originalRule).toEqual(activationRule); });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 66
const activationRule10Gen = gen.object({ id: gen.string, schemaVersion: gen.return(10), ubCode: gen.string, embUuid: gen.string, trigger: gen.oneOf([ gen.object({ name: gen.return('welcome'), parameters: gen.object({ name: gen.return('delay'), value: gen.number }) }), !// !!... ]), !// !!... });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 67
const activationRule10Gen = gen.object({ id: gen.string, schemaVersion: gen.return(10), ubCode: gen.string, embUuid: gen.string, trigger: gen.oneOf([ gen.object({ name: gen.return('welcome'), parameters: gen.object({ name: gen.return('delay'), value: gen.number }) }), !// !!... ]), !// !!... });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 68
const activationRule10Gen = gen.object({ id: gen.string, schemaVersion: gen.return(10), ubCode: gen.string, embUuid: gen.string, trigger: gen.oneOf([ gen.object({ name: gen.return('welcome'), parameters: gen.object({ name: gen.return('delay'), value: gen.number }) }), !// !!... ]), !// !!... });
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 69
yarn add babel-plugin-transform-flow-to-gen !--dev
(https:!//github.com/unbounce/babel-plugin-transform-flow-to-gen)
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 70
!// .babelrc
{ "presets": ["es-2015"], "plugins": ["syntax-flow"], "env": { "development": { "plugins": ["strip-flow-types"] }, "test": { "plugins": ["flow-to-gen", "strip-flow-types"] } } }
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 71
import { AnyActivationRule } from './types';
const activationRuleGen = AnyActivationRule.toGen();
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce
import { AnyActivationRule, MAX_SCHEMA_VERSION } from './types';
check.it('is backwards compatible', AnyActivationRule.toGen(), (activationRule) !=> { const { schemaVersion } = activationRule;
const upgradedRule = upgradeActivationRule( MAX_SCHEMA_VERSION, activationRule );
const originalRule = upgradeActivationRule( schemaVersion, upgradedRule );
expect(originalRule).toEqual(activationRule); } );
72
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce
import { AnyActivationRule, MAX_SCHEMA_VERSION } from './types';
check.it('is backwards compatible', AnyActivationRule.toGen(), (activationRule) !=> { const { schemaVersion } = activationRule;
const upgradedRule = upgradeActivationRule( MAX_SCHEMA_VERSION, activationRule );
const originalRule = upgradeActivationRule( schemaVersion, upgradedRule );
expect(originalRule).toEqual(activationRule); } );
73
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 74
Generating generators generates success
😀
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 75
Locks down type definitions
😀
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce
function someFunc(arr: string[]): string
function someFunc(arr: [string, string, string]): string
76
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce
function someFunc(arr: string[]): string
function someFunc(arr: [string, string, string]): string
77
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 78
Fuzz Testing😀
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce
type State = { !// !!... };
type Action = {type: 'DO_A'} | {type: 'DO_B'};
function reducer(state: State, action: Action): Action;
createStore<State>(reducer, initialState);
79
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce
check.it('is idempotent', sort.toGen(), (args) !=> { expect(sort(sort(!!...args))).toEqual(sort(!!...args)); });
80
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 81
Can't type check test code and non-standard
☹
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 82
Very, very, very slow☹
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 83
No silver bullet☹
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 84
TS and Flow can now force declarations to sync
☹😀
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 85
Property-Based Testing finds flaws in your code.
Lets make it easy to do in JavaScript.
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 87
References:
https:!//fsharpforfunandprofit.com/posts/property-based-testing/
https:!//fsharpforfunandprofit.com/posts/property-based-testing-2/
https:!//!!www.youtube.com/watch?v=shngiiBfD80
http:!//propertesting.com/
https:!//github.com/clojure/test.check
http:!//leebyron.com/testcheck-js/
Background Image:
https:!//!!www.toptal.com/designers/subtlepatterns/memphis-colorful/
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce
import uuid from 'uuid/v4'; import type { $Gen } from 'babel-plugin-transform-flow-to-gen/Gen';
!// type $Gen<T, _U> = T;
type User = { id: $Gen<string, uuid>; name: string; }
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce
!// use higher order type in JS const janitorGen = Worker.toGen(gen.object({type: 'janitor'}));
!// OR create type in Flow type Janitor = Worker<{type: 'janitor'}>; const janitorGen = Janitor.toGen();
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce
import { generator } from 'my-lib'; import { AnyActivationRule } from './types';
const activationRuleGen = generator<AnyActivationRule>();
!// function generator<T>(): T;
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce
const arr = arrayOfRandomNumbers();
const sorted1 = sort(arr.concat(-Number.MIN_VALUE));
const sorted2 = [-Number.MIN_VALUE].concat(sort(arr));
expect(sorted1).toEqual(sorted2);