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. Mee-KeH-LeH Front End Engineer at Facebook Author of React Design

    Patterns and Best Practices Follow me @MicheleBertoli
  2. “I'm upgrading a pretty big codebase from Jest 0.8.2 to

    15.1.1. Call me a hero.” — Michele Bertoli, Sep 2016
  3. “Saddest thing I hear from engineers: we aren't writing tests

    right now because we're focusing on building features” — Dmitrii Abramov, Feb 2017
  4. • Time consuming • Complex to setup and maintain •

    Browser tests are slow • False negatives Testing is hard
  5. • Focus on DX • Works out of the box

    • Complete product • Used by Facebook overview
  6. • Interactive mode • Automatically find tests related to changed

    files • Run tests in parallel processes • Sandboxed test files and automatic state resets FAST
  7. • Create coverage reports • Run your tests within a

    fake DOM implementation • Integrated manual mocking library ALL-IN-ONE
  8. • Error messages are helpful and color coded • Stack

    traces point to the source of problems • Jest runs previously failed tests first ERRORS
  9. • Jest works with any compile-to-JS language • Integrates seamlessly

    with Babel • TypeScript through ts-jest Integrations
  10. npm install --save-dev jest yarn add --dev jest // package.json

    "scripts": { "test": "jest" } Getting Started
  11. expect.extend({ toBeFoo(received) { return { pass: received === 'foo' }

    } }) test('foo is foo', () => expect('foo').toBeFoo()) Custom matchers
  12. Partial objects test('partial objects', () => { const obj =

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

    { foo: 'bar', baz: Math.random() } expect(obj).toEqual( expect.objectContaining({ foo: 'bar', baz: expect.any(Number), }) ) })
  14. test('works with async/await', async () => { const userName =

    await getUserName(4) expect(userName).toEqual('Mark') }) Async / await
  15. MOCK FUNCTIONS test('mock functions', () => { const mock =

    jest.fn(() => 'foo') expect(mock()).toBe('foo') })
  16. // __mocks__/path.js export default { sep: ':' } // spec

    import path from 'path' jest.mock('path') test('mock path', () => expect(path.sep).toBe(':')) Manual mocks
  17. snapshots Capture snapshots of React trees or other serializable values

    to simplify UI testing and to analyze how state changes over time.
  18. example const Button = ({ disabled, text, primary }) =>

    ( <button className={ primary ? 'btn--primary' : 'btn--secondary' } disabled={disabled} >{text}</button> )
  19. enzyme test('enzyme', () => { const wrapper = shallow( <Button

    disabled text="Click me!" primary /> ) expect(wrapper.prop('disabled')).toBe(true) expect(wrapper.hasClass('btn--primary')).toBe(true) expect(wrapper.text()).toBe('Click me!') })
  20. Test renderer test('test-renderer', () => { const tree = renderer.create(

    <Button disabled text="Click me!" primary /> ).toJSON() expect(tree).toMatchSnapshot() })
  21. 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.
  22. example test('enzyme-to-json', () => { const wrapper = shallow( <Button

    disabled text="Click me!" primary /> ) expect(toJson(wrapper)).toMatchSnapshot() })