在测试 onSubmit 时,我想模拟 API 调用而不是进行实际的 API 调用
while testing onSubmit I want to Mock API call instead of making actual API call
我正在使用 formik 和 useDispatch 来实现表单和提交功能。只有当我注释掉 API 调用代码时,我才能测试该操作是否在提交时分派。如果我不注释掉,就会抛出错误。
如何调用模拟 API 调用而不是实际的 API?或者如何将 redux-mock-store 用于 formik+asynchronous API call?
ForgotPassword.js:
<>
<Typography variant='h6'>Recover Password</Typography>
<Formik
initialValues={{ username: '' }}
onSubmit={(values, { setSubmitting }) => {
dispatch(forgotPassword(values.username)).then(() => {
setSubmitting(false)
})
}}
validationSchema={validations}
>
<Form noValidate>
<Field
name='forgotPassword'
render={formikProps => (
<>
<Field
variant='outlined'
margin='normal'
required
fullWidth
id='username'
label='Username'
name='username'
autoComplete='username'
component={TextField}
autoFocus
/>
<Button type='submit' />
</>
)}
/>
</Form>
</Formik>
</>
ForgotPassword.test.js
import React from 'react'
import ForgotPassword from '../../components/public/ForgotPassword'
import { mount } from 'enzyme'
import { Provider } from 'react-redux'
import configureStore from 'redux-mock-store'
import * as ReactReduxHooks from '../react-redux-hooks'
import thunk from 'redux-thunk'
describe('#ForgotPassword', () => {
let wrapper
const middleWare = [thunk]
const mockStore = configureStore(middleWare)
let store
beforeEach(() => {
const initialState = {
auth: { tempUsername: '' },
}
store = mockStore(initialState)
jest
.spyOn(ReactReduxHooks, 'useSelector')
.mockImplementation(state => store.getState())
jest
.spyOn(ReactReduxHooks, 'useDispatch')
.mockImplementation(() => store.dispatch)
wrapper = mount(
<Provider store={store}>
<ForgotPassword />
</Provider>
)
})
it('expect value changes after simulate change', async () => {
wrapper.find('input').simulate('change', {
persist: () => {},
target: { value: 'jhonny123', name: 'username' },
})
expect(wrapper.find('input').props().value).toBe('jhonny123')
wrapper.find('button').simulate('submit')
await new Promise(resolve => {
setTimeout(() => resolve(), 0)
})
const actions = store.getActions()
console.log(actions)
//when the API call part, is commented out inside the actions, prints the actions nicely
//and if I don't comment out the API call part, throws an error
})
})
调度的动作
export const forgotPassword = username => {
return async dispatch => {
dispatch(setTempUsername(username))
// await Auth.forgotPassword(username)
// .then(() => {
// dispatch(setResettingPassword(true))
// })
// .catch(err => {
// /*dispatch(showError(err.message)*/
// })
dispatch(
showSuccess(
'A verification code has been sent to the email linked to your username.'
)
)
}
}
这里是 console.log 当 API 调用部分在动作中被注释掉时
[
{ type: 'auth/setTempUsername', payload: 'jhonny123' },
{
type: 'snackbar/handleSnackbar',
payload: {
verticalPosition: 'bottom',
horizontalPosition: 'center',
message: 'A verification code has been sent to the email linked to your username.',
autoHideDuration: 10000,
messageType: 'success',
isOpen: true
}
}
]
api调用未注释掉时出错
TypeError: Cannot read property 'clientMetadata' of undefined
407 | return async dispatch => {
408 | dispatch(setTempUsername(username))
> 409 | await Auth.forgotPassword(username)
| ^
410 | .then(() => {
411 | dispatch(setResettingPassword(true))
412 | })
从 2.1.0 开始,Redux Thunk 支持注入自定义参数,您可以将其用于您在应用中使用的 apis,例如:
const store = configureStore({
reducer: {
auth: authReducer,
},
middleware: [
getDefaultMiddleware({
thunk: {
extraArgument: storeAuth,
},
}),
],
})
然后在你的 thunk acion 中删除 import Auth 并从第三个参数中提取它:
return async (dispatch, getState, { storeAuth }) => {
dispatch(setTempUsername(username))
await storeAuth.forgotPassword(username)
.then(() => {
dispatch(setResettingPassword(true))
})
要测试它,您应该创建模拟商店并将模拟 api 作为额外参数添加到 thunk
const storeAuth = {
forgotPassword: jest.fn(() => Promise.resolve({ data: {} })),
}
const middleWare = [thunk.withExtraArgument(storeAuth)]
const mockStore = configureStore(middleWare)
我正在使用 formik 和 useDispatch 来实现表单和提交功能。只有当我注释掉 API 调用代码时,我才能测试该操作是否在提交时分派。如果我不注释掉,就会抛出错误。
如何调用模拟 API 调用而不是实际的 API?或者如何将 redux-mock-store 用于 formik+asynchronous API call?
ForgotPassword.js:
<>
<Typography variant='h6'>Recover Password</Typography>
<Formik
initialValues={{ username: '' }}
onSubmit={(values, { setSubmitting }) => {
dispatch(forgotPassword(values.username)).then(() => {
setSubmitting(false)
})
}}
validationSchema={validations}
>
<Form noValidate>
<Field
name='forgotPassword'
render={formikProps => (
<>
<Field
variant='outlined'
margin='normal'
required
fullWidth
id='username'
label='Username'
name='username'
autoComplete='username'
component={TextField}
autoFocus
/>
<Button type='submit' />
</>
)}
/>
</Form>
</Formik>
</>
ForgotPassword.test.js
import React from 'react'
import ForgotPassword from '../../components/public/ForgotPassword'
import { mount } from 'enzyme'
import { Provider } from 'react-redux'
import configureStore from 'redux-mock-store'
import * as ReactReduxHooks from '../react-redux-hooks'
import thunk from 'redux-thunk'
describe('#ForgotPassword', () => {
let wrapper
const middleWare = [thunk]
const mockStore = configureStore(middleWare)
let store
beforeEach(() => {
const initialState = {
auth: { tempUsername: '' },
}
store = mockStore(initialState)
jest
.spyOn(ReactReduxHooks, 'useSelector')
.mockImplementation(state => store.getState())
jest
.spyOn(ReactReduxHooks, 'useDispatch')
.mockImplementation(() => store.dispatch)
wrapper = mount(
<Provider store={store}>
<ForgotPassword />
</Provider>
)
})
it('expect value changes after simulate change', async () => {
wrapper.find('input').simulate('change', {
persist: () => {},
target: { value: 'jhonny123', name: 'username' },
})
expect(wrapper.find('input').props().value).toBe('jhonny123')
wrapper.find('button').simulate('submit')
await new Promise(resolve => {
setTimeout(() => resolve(), 0)
})
const actions = store.getActions()
console.log(actions)
//when the API call part, is commented out inside the actions, prints the actions nicely
//and if I don't comment out the API call part, throws an error
})
})
调度的动作
export const forgotPassword = username => {
return async dispatch => {
dispatch(setTempUsername(username))
// await Auth.forgotPassword(username)
// .then(() => {
// dispatch(setResettingPassword(true))
// })
// .catch(err => {
// /*dispatch(showError(err.message)*/
// })
dispatch(
showSuccess(
'A verification code has been sent to the email linked to your username.'
)
)
}
}
这里是 console.log 当 API 调用部分在动作中被注释掉时
[
{ type: 'auth/setTempUsername', payload: 'jhonny123' },
{
type: 'snackbar/handleSnackbar',
payload: {
verticalPosition: 'bottom',
horizontalPosition: 'center',
message: 'A verification code has been sent to the email linked to your username.',
autoHideDuration: 10000,
messageType: 'success',
isOpen: true
}
}
]
api调用未注释掉时出错
TypeError: Cannot read property 'clientMetadata' of undefined
407 | return async dispatch => {
408 | dispatch(setTempUsername(username))
> 409 | await Auth.forgotPassword(username)
| ^
410 | .then(() => {
411 | dispatch(setResettingPassword(true))
412 | })
从 2.1.0 开始,Redux Thunk 支持注入自定义参数,您可以将其用于您在应用中使用的 apis,例如:
const store = configureStore({
reducer: {
auth: authReducer,
},
middleware: [
getDefaultMiddleware({
thunk: {
extraArgument: storeAuth,
},
}),
],
})
然后在你的 thunk acion 中删除 import Auth 并从第三个参数中提取它:
return async (dispatch, getState, { storeAuth }) => {
dispatch(setTempUsername(username))
await storeAuth.forgotPassword(username)
.then(() => {
dispatch(setResettingPassword(true))
})
要测试它,您应该创建模拟商店并将模拟 api 作为额外参数添加到 thunk
const storeAuth = {
forgotPassword: jest.fn(() => Promise.resolve({ data: {} })),
}
const middleWare = [thunk.withExtraArgument(storeAuth)]
const mockStore = configureStore(middleWare)