Để cấu hình test cho một app react thông thường sẽ không mấy khó khăn, nhưng để cấu hình cho một app sử dụng các kỹ thuật như redux, react-router đòi hỏi thêm một số cài đặt cần thiết khác.

 

Giả sử một ứng dụng react sử dụng redux và react-router của bạn có file App.js như sau:

// App.js
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import { createBrowserHistory } from 'history';

import createRoutes from '../routes/createRoutes'; // your routers

const store = initStore(); // your redux store
const history = createBrowserHistory({ basename: 'http://localhost:8080'});

class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <ConnectedRouter history={history}>
          {this.state.isReady ?
            createRoutes(store, history)
            :
            <p style={{textAlign: 'center', paddingTop: 50}}>Loading...</p>
          }
        </ConnectedRouter>
      </Provider>
    );
  }
}

export default App;

 

Bạn có một container của redux như sau:

// ../containers/TestContainer.js
import TestComponent from '../components/TestComponent.js';
const mapStateToProps = {...}
const mapDispatchToProps = {...}

export default connect(mapStateToProps, mapDispatchToProps)(TestComponent)

 

Bạn muốn viết test cho container trên như sau:

// TestContainer.spec.js
import React from 'react'
import { wait, fireEvent } from 'react-testing-library'
import { createMemoryHistory } from 'history';
import TestContainer from '../TestContainer';
import TestUtil from '../../../../tests/testUtil';
TestUtil.requireJquery()

const history = createMemoryHistory({ basename: 'http://localhost:8080'});
history.location.pathname = '/your/test/page/path' 

const props = {
  history,
  match: {params: {id: null}}
}

const globalState = {
  isLoading: true,
}

const userState = {...}
const pageState = {...}

const initialState = {
  global: globalState,
  user: userState,
  data: pageState,
}

// mock function  within testPage
const fetchMock = jest.spyOn(api, 'fetchMock')
const dumpData = {...} // your dump data for test
fetchMock.mockImplementation(() => Promise.resolve(dumpData))

let testPage = null

beforeAll(async () => {
  testPage = TestUtil.renderWithRedux(<TestContainer {...props} />, {initialState})
  // NOTE: wait to render done
  await wait(() => surgeryPage.getByText(/render successfully/i))
})

it ('should match snapshot', () => {
  expect(testPage).toMatchSnapshot()
})

it ('should active with jquery', async () => {
  // fill 'ab-' to input element
  const $ele = $qDiv.find('.textInputDiv input:eq(0)')
  fireEvent.change($ele.get(0), { target: { value: 'ab-'}})

  await wait(() => {
    // because there has async method in pageTest, wait until errorText class appeared
    const errText = $qDiv.find('.errorText').text()
    expect(errText).toEqual('error')
  })
})

 

Và mình cần viết một module TestUtil như sau:

// testUtil.js
import React from 'react'
import { render } from 'react-testing-library'
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import { MemoryRouter } from 'react-router-dom';
import { createMemoryHistory } from 'history';
const memoryHistory = createMemoryHistory({ basename: 'http://localhost:8080'});

export default class TestUtil {

  static requireJquery() {
    window.$ = require('./jquery.min.js')
  }

  /**
   * This is a handy function that I normally make available for all my tests
   * that deal with connected components.
   * you can provide initialState or the entire store that the ui is rendered with
   */
  static renderWithRedux = (
    component,
    { initialState, store = initStore(initialState), history } = {}
  ) => {
    return {
      ...render(
        <Provider store={store}>
          <ConnectedRouter history={history || memoryHistory}>
            <MemoryRouter>
              {component}
            </MemoryRouter>
          </ConnectedRouter>
        </Provider>
      ),
      // adding `store` to the returned utilities to allow us
      // to reference it in our tests (just try to avoid using
      // this to test implementation details).
      store,
    }
  }
}