我正在为一个组件编写集成测试,该组件应根据异步(重击)redux动作的响应重定向到特定路径。
这是我组件的简化版本:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
redirect: false
}
this.props.dispatch(asyncThunkAction())
.then( () => this.setState({redirec: true}) )
.catch( (err) => console.log('action failed') )
}
...
render() {
if (this.state.redirect) {
return <Redirect to='/whocares' />
}
...
}
}
function mapStateToProps(state) {
return {
...
};
}
export default connect(mapStateToProps)(MyComponent);
我想编写一个测试来断言该组件已重定向到预期路径。
我正在使用this technique来检查实际的重定向路径(虽然不完美,但这不是此问题的重点)。
卡住的地方是在执行{uxux / thunk}操作后.then()
中的状态更改。因为这是一个承诺,所以重定向会在我的expect
语句之后发生,因此我无法对其进行测试。
这是我的测试结果:
const middlewares = [thunk];
const mockStore = configureStore(middlewares);
test('redirects after thunk action', () => {
const redirectUrl = '/whocares'
const data = {};
jest.mock('../actions');
act(() => {
ReactDOM.render(
<TestRouter
ComponentWithRedirection={<MyComponent store={mockStore(data)} />}
RedirectUrl={redirectUrl}
/>,
container);
});
expect(container.innerHTML).toEqual(
expect.stringContaining(redirectUrl)
)
})
我的TestRouter只是将预期的重定向URL打印到DOM中。 (请查看上面的链接以获取有关此hack的完整说明。)因此,现在,我的测试(正确地)没有击中预期的路线,而是正确地识别了在执行重击操作时出现的加载屏幕。
我认为正确的方法是模拟asyncThunkAction
的响应,以便它返回具有匹配数据的已解决的promise,但是到目前为止,我还无法弄清楚怎么做。我遵循manual mocks上的Jest文档,并创建了相应的模拟文件:
// __mocks__/actions.js
const asyncThunkAction = function(){
return Promise.resolve({foo: 'bar'});
};
export { asyncThunkAction };
...但是我的测试仍然“看到”加载状态。我什至不认为它正在查看我的模拟文件/操作。
正确的方法是什么?
答案 0 :(得分:1)
诚然,这是对您问题的横向回答,但是我建议您尝试一下测试库及其体现的理想,尤其是对于集成测试。
它有DOM和React两种版本,可能使用哪种取决于您的重定向发生在哪个抽象级别:
https://github.com/testing-library/dom-testing-library https://github.com/testing-library/react-testing-library
使用此范例,您将不会尝试断言用户已重定向到正确的路径,而是在重定向用户后在屏幕上显示了正确的内容。您还可以将模拟限制在绝对必要的条件下(如果您正在执行真正的集成测试,则可能没有任何东西或只有测试环境无法模仿的浏览器API)。
这里的整体方法可能会让您减少模拟量,或者渲染更大比例的应用程序。您可以在此处找到一个可能有用的示例:https://codesandbox.io/s/github/kentcdodds/react-testing-library-examples/tree/master/?fontsize=14&module=%2Fsrc%2F__tests__%2Freact-router.js&previewwindow=tests
由于此方法中的模拟较少,因此如何实现此目标的细节可能超出了您所给出的示例的范围,但是上面的示例链接对入门起了很大的作用。
答案 1 :(得分:1)
这是我如何使这项工作有效的“秘诀” ...
使用testing-library/react ...
import { render, fireEvent, waitForElement, act } from '@testing-library/react';
(此建议+1到@tmahle)
通过创建一个“ manual mock”来模拟axios(或包装它的API模块),这实际上需要在包含相同名称文件的真实文件旁边创建一个__mocks__
目录。然后导出一个对象,该对象的属性会替换get
方法(或您的代码使用的任何方法)。
//__mocks__/myclient.js
export default {
get: jest.fn(() => Promise.resolve({ data: {} }))
};
即使您没有在测试中调用模拟代码,也需要在测试文件中import
...
import myapi from '../api/myapi';
jest.mock('../api/myai');
您可以像这样通过模拟的API调用模拟响应:
myapi.get.mockResolvedValueOnce({
data: { foo: "bar" },
});
这部分我有点模糊...
即使模拟的API请求以已解决的承诺立即响应,您可能仍需要wait
才能编写expects
const { getByText, getByTestId, container } = render(<MyComponent />);
await wait(() => getByText('Some text that appears after the '));
expect(container.innerHTML).toEqual('whatever');
所有这些都在各种文档和SO问题中都“存在”……但是我花了很长时间才将它们拼凑在一起。希望这可以节省您的时间。