Slide 1

Slide 1 text

Painless Testing @MicheleBertoli

Slide 2

Slide 2 text

Mee-KeH-LeH Front End Engineer at Facebook Author of React Design Patterns and Best Practices Follow me @MicheleBertoli

Slide 3

Slide 3 text

“I'm upgrading a pretty big codebase from Jest 0.8.2 to 15.1.1. Call me a hero.” — Michele Bertoli, Sep 2016

Slide 4

Slide 4 text

Testing

Slide 5

Slide 5 text

“Saddest thing I hear from engineers: we aren't writing tests right now because we're focusing on building features” — Dmitrii Abramov, Feb 2017

Slide 6

Slide 6 text

● Time consuming ● Complex to setup and maintain ● Browser tests are slow ● False negatives Testing is hard

Slide 7

Slide 7 text

● Catch some bugs ● Confidence (Move fast™) ● Refactoring ● Documentation Testing is important

Slide 8

Slide 8 text

“Unpopular opinion: component unit testing is overrated.” — Dan Abramov, Jul 2016

Slide 9

Slide 9 text

“Write tests. Not too many. Mostly integration.” — Guillermo Rauch, Dec 2016

Slide 10

Slide 10 text

Mike Cohn, Nov 2009

Slide 11

Slide 11 text

Dmitrii Abramov, Dec 2016

Slide 12

Slide 12 text

jest

Slide 13

Slide 13 text

● Focus on DX ● Works out of the box ● Complete product ● Used by Facebook overview

Slide 14

Slide 14 text

● Interactive mode ● Automatically find tests related to changed files ● Run tests in parallel processes ● Sandboxed test files and automatic state resets FAST

Slide 15

Slide 15 text

● Create coverage reports ● Run your tests within a fake DOM implementation ● Integrated manual mocking library ALL-IN-ONE

Slide 16

Slide 16 text

● Error messages are helpful and color coded ● Stack traces point to the source of problems ● Jest runs previously failed tests first ERRORS

Slide 17

Slide 17 text

● Jest works with any compile-to-JS language ● Integrates seamlessly with Babel ● TypeScript through ts-jest Integrations

Slide 18

Slide 18 text

● Integrated support for promises and async/await ● Can run asynchronous code synchronously ASYNC

Slide 19

Slide 19 text

Setup

Slide 20

Slide 20 text

npm install --save-dev jest yarn add --dev jest // package.json "scripts": { "test": "jest" } Getting Started

Slide 21

Slide 21 text

yarn add --dev babel-jest babel-preset-es2015 // .babelrc { "presets": ["es2015"] } Modern javascript

Slide 22

Slide 22 text

● automock ● bail ● collectCoverage ● notify ● testEnvironment ● timers And many more. configuration

Slide 23

Slide 23 text

Writing tests

Slide 24

Slide 24 text

test('Hello, Jest!', () => { expect(true).toBe(true) })

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

test(name, fn) test.only(name, fn) describe(name, fn) describe.only(name, fn) Globals

Slide 27

Slide 27 text

afterAll(fn) afterEach(fn) beforeAll(fn) beforeEach(fn) Setup and teardown

Slide 28

Slide 28 text

.toBe(value) .toEqual(value) .toBeDefined() .toBeInstanceOf(Class) .toHaveLength(number) .toThrow(error) expect

Slide 29

Slide 29 text

expect.extend({ toBeFoo(received) { return { pass: received === 'foo' } } }) test('foo is foo', () => expect('foo').toBeFoo()) Custom matchers

Slide 30

Slide 30 text

Partial objects test('partial objects', () => { const obj = { foo: 'bar', baz: Math.random() } expect(obj).toEqual( expect.objectContaining({ foo: 'bar' }) ) })

Slide 31

Slide 31 text

asymmetric matchers test('asymmetric matchers', () => { const obj = { foo: 'bar', baz: Math.random() } expect(obj).toEqual( expect.objectContaining({ foo: 'bar', baz: expect.any(Number), }) ) })

Slide 32

Slide 32 text

test('works with promises', () => { return getUserName(4).then( userName => expect(userName).toEqual('Mark') ) }) promises

Slide 33

Slide 33 text

test('works with async/await', async () => { const userName = await getUserName(4) expect(userName).toEqual('Mark') }) Async / await

Slide 34

Slide 34 text

test('spies', () => { const spy = jest.fn() spy() expect(spy).toHaveBeenCalled() }) SPIES

Slide 35

Slide 35 text

MOCK FUNCTIONS test('mock functions', () => { const mock = jest.fn(() => 'foo') expect(mock()).toBe('foo') })

Slide 36

Slide 36 text

// __mocks__/path.js export default { sep: ':' } // spec import path from 'path' jest.mock('path') test('mock path', () => expect(path.sep).toBe(':')) Manual mocks

Slide 37

Slide 37 text

const spy = jest.fn() const debounced = debounce(spy, 1000) debounced('foo') example

Slide 38

Slide 38 text

test('timer functions', () => { jest.runTimersToTime(999) expect(spy).not.toHaveBeenCalled() jest.runTimersToTime(1) expect(spy).toHaveBeenCalledWith('foo') }) timer FUNCTIONS

Slide 39

Slide 39 text

Snapshots

Slide 40

Slide 40 text

snapshots Capture snapshots of React trees or other serializable values to simplify UI testing and to analyze how state changes over time.

Slide 41

Slide 41 text

example const Button = ({ disabled, text, primary }) => ( {text} )

Slide 42

Slide 42 text

enzyme test('enzyme', () => { const wrapper = shallow( ) expect(wrapper.prop('disabled')).toBe(true) expect(wrapper.hasClass('btn--primary')).toBe(true) expect(wrapper.text()).toBe('Click me!') })

Slide 43

Slide 43 text

Test renderer test('test-renderer', () => { const tree = renderer.create( ).toJSON() expect(tree).toMatchSnapshot() })

Slide 44

Slide 44 text

Human-readable exports[`test test-renderer 1`] = ` Click me! `;

Slide 45

Slide 45 text

update @@ -1,5 +1,5 @@ Click me!

Slide 46

Slide 46 text

watch Watch Usage › Press a to run all tests. › Press o to only run tests related to changed files. › Press u to update failing snapshots. › Press p to filter by a filename regex pattern. › Press q to quit watch mode. › Press Enter to trigger a test run.

Slide 47

Slide 47 text

enzyme-to-json Convert Enzyme wrappers to a format compatible with Jest snapshot testing. adriantoine/enzyme-to-json

Slide 48

Slide 48 text

example test('enzyme-to-json', () => { const wrapper = shallow( ) expect(toJson(wrapper)).toMatchSnapshot() })

Slide 49

Slide 49 text

Coverage

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

webpack

Slide 52

Slide 52 text

CSS MODULES { "jest": { "moduleNameMapper": { "\\.css$": "identity-obj-proxy" } } }

Slide 53

Slide 53 text

resolve.root { "jest": { "modulePaths": [ "/shared/vendor/modules" ] } }

Slide 54

Slide 54 text

Migrating to jest

Slide 55

Slide 55 text

codemods Codemods that simplify migrating JavaScript test files from Mocha, Tape and AVA to Jest. skovhus/jest-codemods

Slide 56

Slide 56 text

Next steps Follow @fbjest Stars are welcome facebook/jest

Slide 57

Slide 57 text

One more thing

Slide 58

Slide 58 text

“The best testing strategy is not writing tests.” — Michele Bertoli, Dec 2016

Slide 59

Slide 59 text

snapguidist Snapshot testing for React Styleguidist. MicheleBertoli/snapguidist

Slide 60

Slide 60 text

Jest packages ● jest-haste-map ● jest-snapshots And many more.

Slide 61

Slide 61 text

react-fix-it Automagically generate tests from errors. MicheleBertoli/react-fix-it

Slide 62

Slide 62 text

Any question?