Replies: 2 comments 2 replies
-
Can you share some code? Would give us a better starting point to help you. |
Beta Was this translation helpful? Give feedback.
0 replies
-
Sure! I was asking what is the recommended way to do it in general first, but yes, might be easier to go directly to my case :) Child machine (subject under test)import { assign, createMachine, sendParent } from 'xstate'
import fetchData, {
DataFetchError,
ResponseData,
} from '../common/api/fetchData'
/** This type of event is only to trigger parent machines, not this one */
export type FetchDoneEvent = {
type: 'FETCH_DONE'
data?: ResponseData
error?: DataFetchError
}
export type DataFetchMachineContext = {
data?: ResponseData
error?: DataFetchError
}
export type FetchRequestEvent = {
type: 'FETCH_REQUEST'
params: {
entityId: string
}
}
export type FetchCancelEvent = {
type: 'FETCH_CANCEL'
}
type FetchResultEvent = {
type: 'done.invoke.dataFetchMachine.loading:invocation[0]'
data: ResponseData
}
type FetchErrorEvent = {
type: 'error.platform.dataFetchMachine.loading:invocation[0]'
data: DataFetchError
}
export type DataFetchMachineEvent =
| FetchRequestEvent
| FetchCancelEvent
| FetchResultEvent
| FetchErrorEvent
const dataFetchMachine = createMachine<
DataFetchMachineContext,
DataFetchMachineEvent
>(
{
id: 'dataFetchMachine',
schema: {
context: {} as DataFetchMachineContext,
events: {} as DataFetchMachineEvent,
services: {} as {
fetchData: {
data: FetchResultEvent['data']
}
},
},
context: {},
initial: 'idle',
predictableActionArguments: true,
states: {
idle: {
on: {
FETCH_REQUEST: {
target: 'loading',
},
},
initial: 'noError',
states: {
noError: {
entry: ['clearError'],
},
errored: {},
},
},
loading: {
on: {
FETCH_REQUEST: {
target: 'loading',
},
FETCH_CANCEL: {
target: 'idle',
},
},
invoke: {
src: 'fetchData',
onDone: {
target: 'idle',
actions: ['assignDataToContext', 'notifyParent'],
},
onError: {
target: 'idle.errored',
actions: ['assignErrorToContext', 'notifyParent'],
},
},
},
},
},
{
services: {
fetchData: (context, event) => {
if (event.type !== 'FETCH_REQUEST') {
throw new Error(
'Incorrect event type (expecting FETCH_REQUEST)',
)
}
return fetchData(event.params.entityId)
},
},
actions: {
assignDataToContext: assign((context, event) => {
if (
event.type !==
'done.invoke.dataFetchMachine.loading:invocation[0]'
) {
throw new Error(
'Incorrect event type (expecting result data)',
)
}
return {
data: event.data,
}
}),
clearError: assign(context => ({
...context,
error: undefined,
})),
assignErrorToContext: assign((context, event) => {
if (
event.type !==
'error.platform.dataFetchMachine.loading:invocation[0]'
) {
throw new Error(
'Incorrect event type (expecting error data)',
)
}
return {
error: event.data,
}
}),
notifyParent: sendParent<
DataFetchMachineContext,
DataFetchMachineEvent,
FetchDoneEvent
>(({ data, error }) => ({
type: 'FETCH_DONE',
data,
error,
})),
},
},
)
export default dataFetchMachine The way it works on the happy path:
|
Beta Was this translation helpful? Give feedback.
2 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
I created a machine for data fetching, which is invoked from another machine. In turn, this child machine invokes a promise (the actual fetch). Manual testing confirmed that it works as expected, but I am having trouble to write unit tests for it (we use Jest).
How do you recommend to test it? I saw discussions where other people mocked
setParent
, but I didn't find it useful for validation because we call it with an action creation function, and anyway, I think we should rather create a dummy parent machine to invoke rather than interpret the data fetch machine directly, yet it is not clear how to command and inspect the subject under test with this setup.Beta Was this translation helpful? Give feedback.
All reactions