在每次测试之前手动修改 initialState 并将其传递给商店?
manually modifying initialState and pass it to the store before every test?
我试图了解反应和 redux 测试的过程,我正在使用测试库来使用 dom 节点查询 来测试我的项目,但是我我仍然对我应该在我的反应项目中测试 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
永远不会像在实际应用程序中那样变为 false!
像这样使用 initialState 是对还是错还是可以以其他方式使用才是正确的??
如果您想要测试 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
到 return 不同的场景并相应地测试它们。
我试图了解反应和 redux 测试的过程,我正在使用测试库来使用 dom 节点查询 来测试我的项目,但是我我仍然对我应该在我的反应项目中测试 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
永远不会像在实际应用程序中那样变为 false!
像这样使用 initialState 是对还是错还是可以以其他方式使用才是正确的??
如果您想要测试 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
到 return 不同的场景并相应地测试它们。