Reactjs 手动修改initialState并在每次测试前将其传递给存储?

Reactjs 手动修改initialState并在每次测试前将其传递给存储?,reactjs,unit-testing,redux,jestjs,react-testing-library,Reactjs,Unit Testing,Redux,Jestjs,React Testing Library,我试图理解react和redux测试的过程,我使用测试库使用dom节点查询来测试我的项目,但我仍然对在react项目中测试redux实现的方式感到困惑: 我从react测试库中创建了一个自定义渲染函数,而不是普通的渲染方法 import React from 'react' import { render as rtlRender } from '@testing-library/react' import { Provider } from 'react-redux' import confi

我试图理解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.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,
});
函数应用程序({fetchData,data:{loading,error,books}){
useffect(()=>{
fetchData()
}, []);
返回(
反应测试
{
错误?错误:
!正在加载?:…正在加载书籍
}
);
}
常量mapStateToProps=状态=>({
数据:state.data,
});
我不确定这样使用initialState是否是一种正确的方法,因为我没有找到任何其他方法在我的测试用例中实现,当我尝试测试使用
waitForElementToBeRemoved
获取数据后加载消息是否会消失时,我遇到了这个问题,因为我总是收到超时错误,指示
加载
与实际应用中的情况不同,永远不会出现错误


是否使用initialState像这样是对的还是错的,或者可以用另一种方式来正确使用???

如果您想测试的是
App.js
的行为,这取决于
获取
结果,那么我会采取不同的方法

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 }]})
    })
  })
});
从“/fetchDataLocation”导入{fetchData};
jest.mock('./fetchDataLocation',()=>({
fetchData:jest.fn()
}))
jest.mock('./错误',()=>jest.fn(()=>Error');
jest.mock('./加载',()=>jest.fn(()=>Loading');
jest.mock('./书店',()=>jest.fn(()=>Bookstore');
描述('App',()=>{
描述('有错误',()=>{
在每个之前(()=>{
错误。mockClear();
Loading.mockClear();
fetchData.mockImplementation(()=>Promise.reject('Error'));
})
测试('呈现加载组件',()=>{
const{container}=render();
expect(Loading).toBeCalled();//或toBeCalledTimes(1)或lastCalledWith(XYZ),如果要测试道具
})
测试('呈现错误组件',()=>{
const{container}=render();
expect(Error).toBeCalled();
})
})
描述('带数据',()=>{
在每个之前(()=>{
Loading.mockClear();
Bookstore.mockClear();
fetchData.mockImplementation(()=>Promise.resolve([{id:2}]);
})
测试('呈现加载组件',()=>{
const{container}=render();
expect(Loading).toBeCalled();//或toBeCalledTimes(1)或lastCalledWith(XYZ),如果要测试道具
})
测试('组件',()=>{
const{container}=render();
expect(Bookstore).lastCalledWith({books:[{id:2}]})
})
})
});

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

如果您想要测试的是
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 }]})
    })
  })
});
从“/fetchDataLocation”导入{fetchData};
jest.mock('./fetchDataLocation',()=>({
fetchData:jest.fn()
}))
jest.mock('./错误',()=>jest.fn(()=>Error');
jest.mock('./加载',()=>jest.fn(()=>Loading');
jest.mock('./书店',()=>jest.fn(()=>Bookstore');
描述('App',()=>{
描述('有错误',()=>{
在每个之前(()=>{
错误。mockClear();
Loading.mockClear();
fetchData.mockImplementation(()=>Promise.reject('Error'));
})
测试('呈现加载组件',()=>{
const{container}=render();
expect(Loading).toBeCalled();//或toBeCalledTimes(1)或lastCalledWith(XYZ),如果要测试道具
})
测试('呈现错误组件',()=>{
const{container}=render();
expect(Error).toBeCalled();
})
})
描述('带数据',()=>{
在每个之前(()=>{
Loading.mockClear();
Bookstore.mockClear();
fetchData.mockImplementation(()=>Promise.resolve([{id:2}]);
})
测试('呈现加载组件',()=>{
const{container}=render();
expect(Loading).toBeCalled();//或toBeCalledTimes(1)或lastCalledWith(XYZ),如果要测试道具
})
测试('组件',()=>{
const{container}=render();
expect(Bookstore).lastCalledWith({books:[{id:2}]})
})
})
});

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

您对initialState的关注点是什么?如果您在测试中按照还原程序所期望的方式设置它,那么就可以了。否则就不是了。这是不可能说的,因为你没有发布Redux部分。在实际的应用程序中,指示加载永远不会变成false-这可能表明他们使用了错误的方式。你到底用waitForElementToBeRemoved做了什么?请提供实际代码。@EstusFlask感谢您的回复,您可以在此处查看完整的redux代码,该代码只是react和redux测试中的示例,因此不重要。您对initialState的关注点是什么?如果您在测试中按照还原程序所期望的方式设置它,那么就可以了。否则就不是了。这是不可能说的,因为你没有发布Redux部分。在实际的应用程序中,指示加载永远不会变成false-这可能表明他们使用了错误的方式。你到底用waitForElementToBeRemoved做了什么?请提供实际代码。@ESTUST
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 }]})
    })
  })
});