property-based testing for godly tests

91
Property-Based Testing for Godly Tests - Gabe Scholz @unbounce !

Upload: garbles

Post on 21-Jan-2018

1.106 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce

!

Page 2: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce

!

Page 3: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 3

! Gabe from Vancouver

🐙 @garbles

📬 [email protected]

Page 4: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce

careers.unbounce.com

4

Page 5: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce

Property-Based Testing for Godly Tests

5

Page 6: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 6

Why do we write tests?

Page 7: Property-Based Testing for Godly 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

Page 8: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 8

Tests prevent regressions!

Page 9: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 9

Tests prevent regressions!(that you know about)

Page 10: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 10

Example-Based Tests

Page 11: Property-Based Testing for Godly 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]); });

Page 12: Property-Based Testing for Godly Tests

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]); });

Page 13: Property-Based Testing for Godly Tests

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]); });

Page 14: Property-Based Testing for Godly Tests

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]); });

Page 15: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 15

Set of all possible inputs

Page 16: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 16

Fuzz Tests

Page 17: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 17

it('does not throw', () !=> { const arr = arrayOfRandomNumbers(); expect(() !=> sort(arr)).not.toThrow(); });

Page 18: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 18

Set of all possible inputs

Page 19: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 19

it('sorts unsorted arrays', () !=> { const arr = arrayOfRandomNumbers(); const sorted = !// ?????? expect(sort(arr)).toEqual(sorted); });

Page 20: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 20

Property-Based Tests

Page 21: Property-Based Testing for Godly 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); } } });

Page 22: Property-Based Testing for Godly Tests

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); } } });

Page 23: Property-Based Testing for Godly Tests

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); } } });

Page 24: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 24

Set of all possible inputs

Page 25: Property-Based Testing for Godly Tests

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

Page 26: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 26

Quickcheck

Page 27: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 27

Testcheck Testcheck.js

Page 28: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 28

Deliberate randomness

Page 29: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 29

arrayOfRandomNumbers(); !// [1, 500, 6012], [45, 12, 579, 77, -8]!!...

Page 30: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 30

Generators

Page 31: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 31

import { gen } from 'testcheck';

gen.string; !// 'a1x', '', 'e4r'!!...

gen.int;

gen.bool;

Page 32: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 32

import { gen } from 'testcheck';

gen.string;

gen.int; !// 0, -1, 3, 1!!...

gen.bool;

Page 33: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 33

import { gen } from 'testcheck';

gen.string;

gen.int;

gen.bool; !// true, true, false!!...

Page 34: Property-Based Testing for Godly Tests

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);

Page 35: Property-Based Testing for Godly Tests

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);

Page 36: Property-Based Testing for Godly Tests

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}]!!...

Page 37: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 37

import { sample, gen } from 'testcheck';

sample(gen.array(gen.int), 20);

Page 38: Property-Based Testing for Godly Tests

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], ];

Page 39: Property-Based Testing for Godly Tests

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);

Page 40: Property-Based Testing for Godly Tests

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 }, !// !!... ];

Page 41: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 41

it('is idempotent', () !=> { const arr = arrayOfRandomNumbers(); expect(sort(sort(arr))).toEqual(sort(arr)); });

Page 42: Property-Based Testing for Godly Tests

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)); });

Page 43: Property-Based Testing for Godly Tests

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)); });

Page 44: Property-Based Testing for Godly Tests

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); } } });

Page 45: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 45

Page 46: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 46

Shrinking

Page 47: Property-Based Testing for Godly Tests

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] } };

Page 48: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 48

Super useful!

Page 49: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 49

Where does it fall apart in JavaScript?

Page 50: Property-Based Testing for Godly Tests

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, });

Page 51: Property-Based Testing for Godly Tests

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, });

Page 52: Property-Based Testing for Godly Tests

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, });

Page 53: Property-Based Testing for Godly Tests

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 };

Page 54: Property-Based Testing for Godly Tests

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 };

Page 55: Property-Based Testing for Godly Tests

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 };

Page 56: Property-Based Testing for Godly Tests

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>;

Page 57: Property-Based Testing for Godly Tests

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>;

Page 58: Property-Based Testing for Godly Tests

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>;

Page 59: Property-Based Testing for Godly Tests

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>;

Page 60: Property-Based Testing for Godly Tests

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;

Page 61: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce

const upgradeActivationRule = (version: number, rule: AnyActivationRule): AnyActivationRule

61

Page 62: Property-Based Testing for Godly Tests

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); });

Page 63: Property-Based Testing for Godly Tests

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); });

Page 64: Property-Based Testing for Godly Tests

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); });

Page 65: Property-Based Testing for Godly Tests

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); });

Page 66: Property-Based Testing for Godly Tests

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 }) }), !// !!... ]), !// !!... });

Page 67: Property-Based Testing for Godly Tests

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 }) }), !// !!... ]), !// !!... });

Page 68: Property-Based Testing for Godly Tests

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 }) }), !// !!... ]), !// !!... });

Page 69: Property-Based Testing for Godly Tests

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)

Page 70: Property-Based Testing for Godly Tests

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"] } } }

Page 71: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 71

import { AnyActivationRule } from './types';

const activationRuleGen = AnyActivationRule.toGen();

Page 72: Property-Based Testing for Godly Tests

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

Page 73: Property-Based Testing for Godly Tests

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

Page 74: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 74

Generating generators generates success

😀

Page 75: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 75

Locks down type definitions

😀

Page 76: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce

function someFunc(arr: string[]): string

function someFunc(arr: [string, string, string]): string

76

Page 77: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce

function someFunc(arr: string[]): string

function someFunc(arr: [string, string, string]): string

77

Page 78: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 78

Fuzz Testing😀

Page 79: Property-Based Testing for Godly Tests

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

Page 80: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce

check.it('is idempotent', sort.toGen(), (args) !=> { expect(sort(sort(!!...args))).toEqual(sort(!!...args)); });

80

Page 81: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 81

Can't type check test code and non-standard

Page 82: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 82

Very, very, very slow☹

Page 83: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 83

No silver bullet☹

Page 84: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 84

TS and Flow can now force declarations to sync

☹😀

Page 85: Property-Based Testing for Godly Tests

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.

Page 86: Property-Based Testing for Godly Tests

Property-Based Testing for Godly Tests - Gabe Scholz @unbounce 86

Thank you!🎉

📬 [email protected]

Page 87: Property-Based Testing for Godly Tests

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/

Page 88: Property-Based Testing for Godly Tests

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; }

Page 89: Property-Based Testing for Godly Tests

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();

Page 90: Property-Based Testing for Godly Tests

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;

Page 91: Property-Based Testing for Godly Tests

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);