我正在尝试在React Native应用中测试异步功能。
class myClass extends React.Component {
...
closeModal = async () => {
if (someCondition) {
await myFunction1();
} else {
await myFunction2();
}
this.props.navigation.state.params.onGoBack();
this.props.navigation.navigate('Main');
};
...
}
这是我的测试
const navigation = {
navigate: jest.fn(),
state: { params: { onGoBack: jest.fn() } },
};
const renderComponent = overrides => {
props = {
navigation,
...overrides,
};
return shallow(< myClass.wrappedComponent {...props} />);
};
describe('When the user presses the close icon', () => {
it('should close the modal', () => {
const wrapper = renderComponent();
const instance = wrapper.instance();
const spyCloseModal = jest.spyOn(instance, 'closeModal');
instance().forceUpdate();
component
.find({ testID: 'close-icon' })
.props()
.onPress();
expect(spyCloseModal).toHaveBeenCalled(); // this is passed
expect(navigation.navigate).toHaveBeenCalled(); // this is not passed
});
});
似乎卡在了await调用中。如果我删除了等待呼叫,那么它将过去。有人在另一篇文章中提到在spyOn之后使用.and.callThrough
,但这给了我这个错误
无法读取未定义的属性“ callThrough”
答案 0 :(得分:0)
解决方案之一是进行测试async
,然后运行await (anything)
将测试分为几个微任务:
it('should close the modal', async () => {
const wrapper = renderComponent();
component
.find({ testID: 'close-icon' })
.props()
.onPress();
await Promise.resolve();
expect(navigation.state.params.onGoBack).toHaveBeenCalled();
expect(navigation.navigate).toHaveBeenCalledWith("Main");
});
我相信您不需要实例方法上的.forceUpdate
或.spyOn
。导航正确完成后,无论调用哪种内部方法都没关系
有关微任务与宏任务的更多信息:https://abc.danch.me/microtasks-macrotasks-more-on-the-event-loop-881557d7af6f
替代方法是使用macrotask(setTimeout(...., 0)
)
it('should close the modal', (done) => {
const wrapper = renderComponent();
component
.find({ testID: 'close-icon' })
.props()
.onPress();
setTimeout(() => {
expect(navigation.state.params.onGoBack).toHaveBeenCalled();
expect(navigation.navigate).toHaveBeenCalledWith("Main");
done();
});
}
答案 1 :(得分:0)
是的,您的方向正确……问题是closeModal
是异步的。
在执行返回测试时,await
尚未完成,因此尚未调用this.props.navigation.navigate
。
在断言closeModal
被调用之前,测试需要等待navigate
完成。
closeModal
是async
函数,因此它将返回Promise
...
...并且您可以使用间谍来检索它返回的Promise
...
...然后,您可以在测试中在该await
上调用Promise
,以确保closeModal
在声明navigate
被调用之前已经完成。
以下是一个简化的工作示例,可以帮助您入门:
import * as React from 'react';
import { shallow } from 'enzyme';
class MyClass extends React.Component {
closeModal = async () => {
await Promise.resolve();
this.props.navigation.navigate('Main');
}
render() { return <div onClick={() => this.closeModal()}></div> }
}
test('MyClass', async () => { // <= async test function
const props = { navigation: { navigate: jest.fn() }};
const wrapper = shallow(<MyClass {...props} />);
const instance = wrapper.instance();
const spyCloseModal = jest.spyOn(instance, 'closeModal');
wrapper.find('div').simulate('click');
expect(spyCloseModal).toHaveBeenCalled(); // Success!
const promise = spyCloseModal.mock.results[0].value; // <= get the Promise returned by closeModal
await promise; // <= await the Promise
expect(props.navigation.navigate).toHaveBeenCalled(); // Success!
})
请注意使用mockFn.mock.results
来获取Promise
返回的closeModal
。