React and the Three Layers of Testing

A presentation at React Amsterdam Meetup in October 2017 in Amsterdam, Netherlands by Bart Waardenburg

Slide 1

Slide 1

The three layers of testing @bartwaardenburg

Slide 2

Slide 2

“We want to deliver high quality applications for our users while regularly releasing new features”

Slide 3

Slide 3

Slide 4

Slide 4

Slide 5

Slide 5

00 Static analysis

Slide 6

Slide 6

Static analysis

Slide 7

Slide 7

“Static analysis is the analysis of software that is performed without actually executing programs”

Slide 8

Slide 8

“Using Flow or TypeScript could have prevented 15% of the public bugs for public projects on GitHub” http:// ttendency.cs.ucl.ac.uk /projects/ type_study /documents/ type_study.pdf

Slide 9

Slide 9

type MyCustomButtonProps = { text : string }; const MyCustomButton = ({ text }: MyCustomButtonProps ) => ( <button> { text } </button> ); const ButtonContainer = () => ( < MyCustomButton text

{ [ 'I' , 'like' , 'turtles' ] } /> );

Slide 10

Slide 10

Slide 11

Slide 11

“ You can have every single variable and function completely typed and linted but still have none of your functions doing what they should be doing”

Slide 12

Slide 12

const a : number

1 ; const b : number

2 ; const multiply = ( a : number , b : number ): number => a + b ; multiply ( a , b );

Slide 13

Slide 13

01 00 Static analysis Unit testing

Slide 14

Slide 14

Unit testing

Slide 15

Slide 15

“A unit test is a way of testing a unit

the smallest piece of code that can be logically isolated in a system”

Slide 16

Slide 16

const multiply = ( a : number , b : number ): number => a + b ; test ( 'Multiply should return the arguments multiplied' , () => { expect ( multiply ( 4 , 3 )). toBe ( 12 ); )};

Slide 17

Slide 17

expect( received ). toBe ( expected ) Expected value to be (using ===): 12 Received: 7

Slide 18

Slide 18

“ A snapshot test verifies that a piece of functionality works the same as it did when the snapshot was created ”

Slide 19

Slide 19

const Button = ({ type }: ButtonProps ) => ( <button className

{ ` btn

${ type }` } /> ); test ( 'The Button component renders correctly' , () => { const component

renderer . create ( <Button type

”good" /> ). toJSON (); expect ( component ). toMatchSnapshot (); });

Slide 20

Slide 20

PASS src / unit

test.spec.js ✓ The Button component renders correctly (11ms)

Slide 21

Slide 21

FAIL src / unit

test.spec.js ✕ The Button component renders correctly (15ms) ● The Button component renders correctly expect( value ). toMatchSnapshot () Received value does not match stored snapshot 1 .

Snapshot

  • Received <button

className =" btn

good" + className =" btn

bad" />

Slide 22

Slide 22

“ You can have every single component and function unit test passing but still have none of your functions working together like they should”

Slide 23

Slide 23

const multiply = ( a : number , b : number ): number => a * b ; const alertNumber = ( value : number ): void => alert ( value ); const ButtonWhichShouldAlertOnClick = () => ( <button onClick

{ () => multiply ( 1 , 2 ) } onMouseEnter

{ () => alertNumber ( multiply ( 1 , 2 )) }

Multiply </button> );

Slide 24

Slide 24

01 02 00 Static analysis Unit testing Integration testing

Slide 25

Slide 25

Integration testing

Slide 26

Slide 26

“Integration testing is the phase in software testing in which individual software modules are combined and tested as a group”

Slide 27

Slide 27

import { mount } from 'enzyme' ; const ButtonWhichShouldAlertOnClick = () => ( < button onClick

{ () => multiply ( 1 , 2 ) } onMouseEnter

{ () => alertNumber ( multiply ( 1 , 2 )) }

Multiply </button> ); alertNumber

jest . fn (); test ( 'The Button component should run a function on click' , () => { const component

mount ( <Button type

"test" /> ); component . find ( 'button' ). simulate ( 'click' ); expect ( alertNumber ). toHaveBeenCalledTimes ( 1 ); });

Slide 28

Slide 28

FAIL src / integration

test.spec.js ✕ The Button component should run a function on click (22ms) ● The Button component should run a function on click expect( jest.fn () ). toHaveBeenCalledTimes ( 1 ) Expected mock function to have been called one time , but it was called zero times .

Slide 29

Slide 29

“ You can have everything working together completely as intended but still have an empty screen for an application”

Slide 30

Slide 30

const multiply = ( a : number , b : number ): number => a * b ; const alertNumber = ( value : number ): void => alert ( value ); const Button = () => ( <button onClick

{ () => alertNumber ( multiply ( 1 , 2 )) }

Multiply </button> ); document . querySelector (‘ body' ). style . cssText

'display: none' ;

Slide 31

Slide 31

01 02 03 00 Static analysis Unit testing Integration testing User interface testing

Slide 32

Slide 32

User interface testing

Slide 33

Slide 33

“User interface testing is the process of testing a product's graphical user interface to ensure it meets its specifications”

Slide 34

Slide 34

t ools • Protractor • Cypress • Puppeteer • Codecept • Navalia • Chromeless • Selenium • Nightmare • Nightwatch • Te s t C a f e • CasperJS • Te s t C a f e

Slide 35

Slide 35

import { Chrome } from ' navalia ' ; import { toMatchImageSnapshot } from 'jest

image

snapshot' ; expect . extend ({ toMatchImageSnapshot }); const chrome

new Chrome (); test ( 'The routing input component should display as expected' , async () => { await chrome . goto ( 'https:// www.anwb.nl / verkeer / routeplanner ' ); await chrome . wait ( '.ROVE

routing

input' ); const screenshot

await chrome . screenshot ( '.ROVE

routing

input' ); await chrome . done (); expect ( screenshot ). toMatchImageSnapshot (); });

Slide 36

Slide 36

Slide 37

Slide 37

PASS src /components/routing

input

address/tests/ RoutingInputAddress.ui

test.js ( 7.153s ) ✓ The routing input component should display as expected (3671ms)

Slide 38

Slide 38

Slide 39

Slide 39

FAIL src /components/routing

input/tests/ RoutingInput.ui

test.js ( 9.909s ) ✕ The routing input component should display as expected (9033ms) ● The routing input component should display as expected Expected image to match or be a close match to snapshot. See diff for details: /Users/p279825/Sites/ANWB/traffic/ src /components/routing

input/tests/__ image_snapshots / diff_output __/routing

input

ui

test

js

the

routing

input

component

should

display

as

expected

1

diff.png

Slide 40

Slide 40

Slide 41

Slide 41

import { Chromeless } from ' chromeless ' ; const chromeless

new Chromeless (); const screenshot

await chromeless . goto ( 'https://www

ontw.anwb.nl / verkeer / routeplanner ' ) . screenshot ( '#routing' , { base64 : true , }); const file

new Buffer ( screenshot , 'base64' ); expect ( file ). toMatchImageSnapshot (); await chromeless . end ();

Slide 42

Slide 42

Slide 43

Slide 43

const chromeless

new Chromeless ({ remote : { endpointUrl : 'https://XXXXXXXXXX.execute

api.eu

west

1.amazonaws.com/dev’, apiKey : 'your

api

key

here’, }, });

Slide 44

Slide 44

“With chromeless you can run hundreds of browsers in parallel”

Slide 45

Slide 45

“You can easily execute > 100.000 tests for free in the free tier ”

Slide 46

Slide 46

01 02 03 00 Static analysis Unit testing Integration testing User interface testing

Slide 47

Slide 47

t hanks & happy testing @bartwaardenburg