Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Testing, tEstIng, TeStInG or how to test React apps with generated input data

Testing, tEstIng, TeStInG or how to test React apps with generated input data

Olena Sovyn

April 25, 2020
Tweet

More Decks by Olena Sovyn

Other Decks in Programming

Transcript

  1. Olena Sovyn
    @frontendgirl
    Testing, tEstIng, TeStInG
    or how to test React apps with generated input data

    View Slide

  2. Olena Sovyn
    • Senior software engineer/tech lead/
    notorious problem solver and
    mighty fighter of tech debt
    • Londoner
    • ❤ traveling and reading. Cooking
    better than 90% of the restaurants
    that provide take away
    https://frontendgirl.com/

    View Slide

  3. Testing

    View Slide

  4. ⚠ Simplifications detected ⚠

    View Slide

  5. e2e testing

    View Slide

  6. Morning Routine Planner
    Time needed: 45 min
    drink coffee
    yoga
    brush teeth
    cook breakfast

    View Slide

  7. Testing
    describe('total time is equal', () => {
    it('40 min when yoga is selected', () => {
    ...
    });
    it('15 min when drink coffee is selected', () => {
    ...
    });
    it('5 min when brush teeth is selected', () => {
    ...
    });
    it('20 min when cook breakfast is selected', () => {
    ...
    });
    });

    View Slide

  8. Morning Routine Planner
    Time needed: 45 min
    drink coffee
    yoga
    brush teeth
    cook breakfast

    View Slide

  9. Morning Routine Planner
    boolean
    boolean
    boolean
    boolean
    Data Types

    View Slide

  10. Morning Routine Planner
    true, false
    true, false
    true, false
    true, false
    Possible Values

    View Slide

  11. Morning Routine Planner
    All possible variations
    [[true, true, true, true], [true, true, true, false], [true, true, false, true],
    [true, true, false, false], [true, false, true, true], [true, false, true, false],
    [true, false, false, true], [true, false, false, false], [false, true, true, true],
    [false, true, true, false], [false, true, false, true], [false, true, false, false],
    [false, false, true, true], [false, false, true, false], [false, false, false, true],
    [false, false, false, false]];

    View Slide

  12. Cartesian Product

    View Slide

  13. Cartesian Product

    View Slide

  14. Cartesian Product
    describe('total time is equal', () => {
    cartesian(...new Array(4).fill([true, false])).forEach(
    ([isYoga, isCoffee, areTeeth, isBreakfast]) => {
    it('...', () => {
    ...
    });
    });
    });

    View Slide

  15. Best explanation found with Google help
    “Property based testing is a technique widely used in the
    Haskell community. The idea consists in automatically
    generating inputs for testing a function.”

    View Slide

  16. Reducers

    View Slide

  17. Flux Reducer
    const reducer = (state = initialState, action) => {
    switch action.type {
    case 'ACTION_1': {
    ...
    return state1;
    }
    case 'ACTION_2': {
    ...
    return state2;
    }
    default: {
    return state;
    }
    }
    }

    View Slide

  18. Unit tests for flux reducer
    describe('reducer', () => {
    it('will change state to state 1 on action 1', () => {
    expect(reducer(initialState, {
    type: 'ACTION_1',
    payload: payload1
    })).toEqual(state1);
    });
    it('will change state to state 2 on action 2', () => {
    expect(reducer(initialState, {
    type: 'ACTION_2',
    payload: payload2
    })).toEqual(state2);
    });
    });

    View Slide

  19. Unit tests for flux reducer
    describe('reducer', () => {
    it('will change state to state 1 on action 1', () => {
    expect(reducer(initialState, {
    type: 'ACTION_1',
    payload: payload1
    })).toEqual(state1);
    });
    it('will change state to state 2 on action 2', () => {
    expect(reducer(initialState, {
    type: 'ACTION_2',
    payload: payload2
    })).toEqual(state2);
    });
    });

    View Slide

  20. Flux Reducer
    const reducer = (state = initialState, action) => {
    switch action.type {
    case 'ACTION_1': {
    ...
    return state1;
    }
    case 'ACTION_2': {
    ...
    return state2;
    }
    default: {
    return state;
    }
    }
    }

    View Slide

  21. Flux Reducer with annotations
    type State = {
    isOnSale: boolean,
    size: 'XS' | 'S' | 'M' | 'L' | 'XL',
    price: number
    }
    const reducer = (state: State = initialState, action: Action) => {
    switch action.type {
    case 'ACTION_1': {
    ...
    return state1;
    }
    case 'ACTION_2': {
    ...
    return state2;
    }
    default: {
    return state;
    }
    }
    }

    View Slide

  22. Flux Reducer with annotations
    type State = {
    isOnSale: boolean,
    size: 'XS' | 'S' | 'M' | 'L' | 'XL',
    price: number
    }
    const reducer = (state: State = initialState, action: Action) => {
    switch action.type {
    case 'ACTION_1': {
    ...
    return state1;
    }
    case 'ACTION_2': {
    ...
    return state2;
    }
    default: {
    return state;
    }
    }
    }

    View Slide

  23. State type
    type State = {
    isOnSale: boolean,
    size: 'XS' | 'S' | 'M' | 'L' | 'XL',
    price: number
    }

    View Slide

  24. State type
    const possibleValues = {
    isOnSale: [true, false],
    size: ['XS', 'S', 'M', 'L', 'XL'],
    price: [number1, number2, number3]
    }

    View Slide

  25. Testing
    describe('reducer', () => {
    it('will change state to state 1 on action 1', () => {
    cartesian([true, false],
    ['XS', 'S', 'M', 'L', 'XL'],
    [number1, number2, number3]
    ).map((isOnSale, size, price) => ({
    isOnSale,
    size,
    price
    })).forEach(stateSample => {
    expect(reducer(stateSample, {
    type: 'ACTION_1',
    payload: payload1
    })).toEqual(state1);
    })
    });
    });

    View Slide

  26. Automation?

    View Slide

  27. Unit tests template generation ?
    type State = {
    isOnSale: boolean,
    size: 'XS' | 'S' | 'M' | 'L' | 'XL',
    price: number
    }
    parsing

    View Slide

  28. Unit tests template generation ?
    describe('reducer', () => {
    it('will change state to state 1 on action 1', () => {
    cartesian([true, false],
    ['XS', 'S', 'M', 'L', 'XL'],
    [number1, number2, number3]
    ).map((isOnSale, size, price) => ({
    isOnSale,
    size,
    price
    })).forEach(stateSample => {
    ...
    });
    });
    });
    generation

    View Slide

  29. Yes, you! ;)
    Thank you!

    View Slide