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

Painless Testing

Painless Testing

Testing UIs has always been a hard job. In the components era, there are new tools available that transform completely the way we approach testing. One of the hottest is Jest, and we'll see how it provides an all-in-one testing solution that saves us time and headache.

Michele Bertoli

February 09, 2017
Tweet

More Decks by Michele Bertoli

Other Decks in Programming

Transcript

  1. Painless Testing
    @MicheleBertoli

    View Slide

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

    View Slide

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

    View Slide

  4. Testing

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  10. Mike Cohn, Nov 2009

    View Slide

  11. Dmitrii Abramov, Dec 2016

    View Slide

  12. jest

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  19. Setup

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  23. Writing tests

    View Slide

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

    View Slide

  25. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  39. Snapshots

    View Slide

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

    View Slide

  41. example
    const Button = ({ disabled, text, primary }) => (
    className={
    primary ? 'btn--primary' : 'btn--secondary'
    }
    disabled={disabled}
    >{text}
    )

    View Slide

  42. 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!')
    })

    View Slide

  43. Test renderer
    test('test-renderer', () => {
    const tree = renderer.create(

    ).toJSON()
    expect(tree).toMatchSnapshot()
    })

    View Slide

  44. Human-readable
    exports[`test test-renderer 1`] = `
    className="btn--primary"
    disabled={true}>
    Click me!

    `;

    View Slide

  45. update
    @@ -1,5 +1,5 @@
    - className="btn--primary"
    + className="button--primary"
    disabled={true}>
    Click me!

    View Slide

  46. 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.

    View Slide

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

    View Slide

  48. example
    test('enzyme-to-json', () => {
    const wrapper = shallow(

    )
    expect(toJson(wrapper)).toMatchSnapshot()
    })

    View Slide

  49. Coverage

    View Slide

  50. View Slide

  51. webpack

    View Slide

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

    View Slide

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

    View Slide

  54. Migrating to jest

    View Slide

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

    View Slide

  56. Next steps
    Follow @fbjest
    Stars are welcome facebook/jest

    View Slide

  57. One more thing

    View Slide

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

    View Slide

  59. snapguidist
    Snapshot testing for React Styleguidist.
    MicheleBertoli/snapguidist

    View Slide

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

    View Slide

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

    View Slide

  62. Any question?

    View Slide