← Back to journalTechnical

Cài đặt test trong react sử dụng redux và react-router

Cách cài đặt test trong react sử dụng redux và react-router

Cài đặt test trong react sử dụng redux và react-router

Tưởng tượng là dạng thức tối cao của nghiên cứu.

Albert Einstein

Để 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,
    }
  }
}