在测试 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)