在每次测试之前手动修改 initialState 并将其传递给商店?

Posted

技术标签:

【中文标题】在每次测试之前手动修改 initialState 并将其传递给商店?【英文标题】:manually modifying initialState and pass it to the store before every test? 【发布时间】:2021-01-27 18:51:45 【问题描述】:

我正在尝试了解 react 和 redux 测试的过程,我正在使用测试库来使用 dom 节点查询 来测试我的项目,但我仍然对我应该测试的方式感到困惑我的 react 项目中的 redux 实现:

我创建了一个自定义渲染函数,而不是来自 react 测试库的普通渲染方法

import React from 'react'
import  render as rtlRender  from '@testing-library/react'
import  Provider  from 'react-redux'
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
const middlewares = [thunk]
const mockStore = configureMockStore(middlewares);

//test-utils.js
//creating a custom render function so we can wrap our App with
//react-redux provider
const render = (ui, initialState) => 
  const store = mockStore(initialState);
  //App wrapper and mocked store passed to it
  const Wrapper = ( children ) => 
    return <Provider store=store>children</Provider>
  
  return rtlRender(ui,  wrapper: Wrapper )


// re-export everything
export * from '@testing-library/react'
// override render method
export  render 

App.test.js 中,我正在手动操作 initialState。这是令人困惑的一部分,我不知道我是否在这里做:

describe('App', () => 
  const  getByText, getByTestId, findByText, queryByText  = screen;

  let initialState = 
    data: 
      books: [],
      error: '',
      loading: false
    ,
    //rest of the state
  

  it('should render App correctly with given redux state', () => 
    const  container  = render(<App />, initialState);
    expect(container.firstChild).toMatchSnapshot();
    expect(getByTestId(/header/)).toHaveTextContent('React Testing')
  );

  it('displays loading message before data get fetched', () => 
    initialState = 
      ...initialState,
      data: 
        ...initialState.data,
        loading: true
      
    
    render(<App />, initialState);
    expect(getByText(/...loading books/)).toBeInTheDocument();
  );

   it('display an error message if any thing wrong happened while fetching data', () => 
     initialState = 
       ...initialState,
       data: 
         ...initialState.data,
         error: 'something went wrong'
       
     
     render(<App />, initialState);
     expect(getByText(/something went wrong/)).toBeInTheDocument();
   )
)

这是例如我在 App 组件

中调用的动作创建者
export const fetchData = () => dispatch => 
    dispatch( type: SET_LOADING ); // this set loading to true

    return axios.get("https://api.jsonbin.io/b/57d5760ea")
        .then(res => 
            dispatch(
                type: FETCH_DATA, // this set data
                payload: res.data.books
            );
            dispatch( type: STOP_LOADING )
        )
        .catch(err => 
            dispatch(
                type: SET_ERROR, // this set errors
                payload: 'Something went wrong'
            )
        )

这是 App.js 组件

function App( fetchData, data:  loading, error, books  ) 
  useEffect(() => 
   fetchData()
  , []);

  return (
    <div className="App">
      <header data-testid="header">
        <h2>React Testing</h2>
        <Bag />
      </header>
      
        error ? error :
          !loading ? <Bookstore books=books /> : <span data-testid='loading-message'>...loading books</span>
      
    </div>
  );


const mapStateToProps = state => (
  data: state.data,
);

我不确定使用这样的 initialState 是否是正确的方法,因为我没有在我的测试用例中找到任何其他实现方法,并且当我尝试测试是否加载消息时遇到了问题使用waitForElementToBeRemoved 获取数据后将消失,因为我总是收到超时错误,指示loading 永远不会像在实际应用中那样变为假!

像这样使用initialState是对还是错,或者可以以其他方式使用是正确的??

【问题讨论】:

您对 initialState 的关注点到底是什么?如果您按照减速器的预期方式在测试中设置它,那么就可以了。否则不是。这是不可能的,因为你没有发布 Redux 部分。 表明加载永远不会像在实际应用中那样为假 - 这可能表明他们以错误的方式使用它。您究竟尝试了 waitForElementToBeRemoved 什么?请提供实际代码。 【参考方案1】:

如果您想要测试的是 App.js 的行为取决于 fetch 结果,那么我会以不同的方式处理它。

import  fetchData  from './fetchDataLocation';

jest.mock('./fetchDataLocation', () => (
  fetchData: jest.fn()
))

jest.mock('./Error', () => jest.fn(() => 'Error'));
jest.mock('./Loading', () => jest.fn(() => 'Loading'));
jest.mock('./Bookstore', () => jest.fn(() => 'Bookstore'));

describe('App', () => 
  describe('with error', () => 
    beforeEach(() => 
      Error.mockClear();
      Loading.mockClear();
      fetchData.mockImplementation(() => Promise.reject('Error'));
    )

    test('renders loading component', () => 
      const  container  = render(<App />);
      expect(Loading).toBeCalled(); // or toBeCalledTimes(1) or lastCalledWith(XYZ) if you want to test the props
    )

    test('renders error component', () => 
      const  container  = render(<App />);
      expect(Error).toBeCalled();
    )
  )

  describe('with data', () => 
    beforeEach(() => 
      Loading.mockClear();
      Bookstore.mockClear();
      fetchData.mockImplementation(() => Promise.resolve([ id: 2 ]));
    )

    test('renders loading component', () => 
      const  container  = render(<App />);
      expect(Loading).toBeCalled(); // or toBeCalledTimes(1) or lastCalledWith(XYZ) if you want to test the props
    )

    test('renders bookstore component', () => 
      const  container  = render(<App />);
      expect(Bookstore).lastCalledWith( books: [ id: 2 ])
    )
  )
);

保持关注点分离很重要,Foo 组件只需要关心它的行为取决于道具。如果组件具有像 fetch 这样的副作用,则模拟 fetch 以返回不同的场景并进行相应的测试。

【讨论】:

以上是关于在每次测试之前手动修改 initialState 并将其传递给商店?的主要内容,如果未能解决你的问题,请参考以下文章

Spring整合junit测试

Spring整合junit测试

server下修改项目发布位置,存放到tomcatwebapps下(项目发布之前)

jmeter性能测试

vm虚拟机Linux每次开机需要手动打开网卡

Selenium2 + Python3.6实战:生成HTML测试报告 Invalid argument