Repozytorium Web Developera

React testing - Jest & Enzyme

(in Polish) Pisanie testów jest zawsze opłacalne - po pewnym czasie zamiast pisania nowego kodu, będziesz głównie zmieniał kod już istniejący, a to może prowadzić do wielu błędów - z testami jest to zdecydowanie szybsze.

Configuration with `create-react-app`

Jest debugging

Put below line in your code:


debugger;

Then run process:


node --inspect-brk node_modules/.bin/jest --runInBand

And open debugger in your browser chrome://inspect.

You can also use config for your editor, i.e. for VSCode while writing `create-react-app`:


{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug CRA Tests",
      "type": "node",
      "request": "launch",
      "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/react-scripts",
      "args": ["test", "--runInBand", "--no-cache", "--env=jsdom"],
      "cwd": "${workspaceRoot}",
      "protocol": "inspector",
      "console": "integratedTerminal",
      "internalConsoleOptions": "neverOpen"
    }
  ]
}

Jest methods

Async/await tests

We have to specify number of expected assertions:


test('the data is peanut butter', async () => {
  expect.assertions(1)
  await expect(fetchData()).resolves.toBe('peanut butter')
})

test('the fetch fails with an error', async () => {
  expect.assertions(1)
  await expect(fetchData()).rejects.toMatch('error')
})

Jest mocks

If you are using a module located in src/models/User.js, you can write a mock in src/models/__mocks__/User.js. Then in file which imports this module, after imports add a line jest.mock('src/models/User.js') which will Jest about using mock instead of a original module.

What's important, you don't need to write your mocks in __mocks__ folder for ES classes. They can be automatically mocked by Jest by simply putting the line jest.mock(MODULE_PATH) to your test file.

Enzyme methods


import React from 'react'
import { shallow, mount, render } from 'enzyme'

import Foo from '../Foo'

describe('A suite', function() {
  it('should render without throwing an error', function() {
    expect(shallow(<Foo />).contains(<div className="foo">Bar</div>)).toBe(true)
  })

  it('should be selectable by class "foo"', function() {
    expect(shallow(<Foo />).is('.foo')).toBe(true)
  })

  it('should mount in a full DOM', function() {
    expect(mount(<Foo />).find('.foo').length).toBe(1)
  })

  it('should render to static HTML', function() {
    expect(render(<Foo />).text()).toEqual('Bar')
  })
})

mount vs shallow

While testing HOCs, do not use mount instead of shallow. Use .dive() with shallow instead. Mount renders all children as well which isn't good for unit tests. This methods are especially needed when testing Recompose.

Tests for Recompose - an example


// Toolbox.js

export const enhance = compose(
  withState('labelText', 'setLabelText', ''),
  withState('hover', 'hovering', false),
  withState('labelLeft', 'setLabelLeftPosition', 0)
)

export Toolbox = /* ... */

export default enhance(Toolbox)

// Toolbox.test.js

import { Toolbox as ToolboxComponent, enhance } from './Toolbox'

const ToolboxClass = toClass(ToolboxComponent) // otherwise won't work with stateless
const Toolbox = enhance(ToolboxClass)

const wrapper = mount()

console.log(wrapper.find(ToolboxClass).props())