我试图测试状态转换发生时是否调用了promise。
我遵循了the official xState tutorial中概述的方法,但是出现以下错误
Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Timeout
这是我的状态机,当您从初始状态过渡时,它所做的就是调用一个Promise。
export const statsMachine = Machine(
{
id: 'stats',
initial: 'incomplete',
states: {
incomplete: {
on: {
MODAL_OPENED: 'loading',
},
},
loading: {
invoke: {
id: 'setRatioDefaultsInFirebase',
src: (context, event) => setStatDefaults(event.payload && event.payload.userId),
onDone: {
target: 'modal',
},
onError: {
target: 'incomplete',
},
},
},
modal: {...}
}
})
这是我的测试。而不是像本教程中那样触发真正的api调用,我想模拟我的api调用。我在开玩笑来模拟副作用。我想断言是嘲笑的副作用被调用了。但是我得到上面列出的错误。
jest.mock('../statsAPI');
test('stats should start off with minimum ratios', done => {
setStatDefaults.mockResolvedValueOnce();
const statsBoxService = interpret(statsMachine)
.onTransition(state => {
if (state.matches({ selected: 'modal' })) {
expect(setStatDefaults).toHaveBeenCalled();
done();
}
})
.start();
statsBoxService.send('MODAL_OPENED');
});
我必须更改什么才能断言在机器转换时我的嘲笑副作用被调用了?
我认为这可能与您的if语句错误一样简单:
if (state.matches({ selected: 'modal' })) {
应该是
if (state.matches('modal')) {
在示例中,'initial','loading','loaded','failed'是状态为'selected'的子级>
话虽这么说,但我还是在研究您的示例,发现它可以正常工作,在模拟方面与您的实现稍有不同:
machines.test.ts:
import { interpret } from 'xstate'; import { statsMachine } from './machines'; test('stats should start off with minimum ratios', done => { global.fetch = jest.fn().mockImplementation( () => Promise.resolve({ json: () => Promise.resolve({}) }) ); const statsBoxService = interpret(statsMachine) .onTransition(state => { if (state.matches('modal')) { expect(global.fetch).toHaveBeenCalledTimes(1); done(); } }) .start(); statsBoxService.send('MODAL_OPENED'); });
machines.ts:
import { Machine } from 'xstate';
export const setStatDefaults = async (t: any) => {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
return response.json();
};
export const statsMachine = Machine(
{
id: 'stats',
initial: 'init',
states: {
init: {
on: {
MODAL_OPENED: 'loading',
}
},
incomplete: {
on: {
MODAL_OPENED: 'loading',
}
},
loading: {
invoke: {
id: 'setRatioDefaultsInFirebase',
src: (context, event) => setStatDefaults(event.payload && event.payload.userId),
onDone: {
target: 'modal',
},
onError: {
target: 'incomplete',
},
},
},
modal: {
}
}
});