在测试onSubmit时,我想模拟API调用,而不是进行实际的API调用

时间:2019-12-03 02:48:04

标签: reactjs redux jestjs formik redux-mock-store

我正在使用formik和useDispatch用于表单和提交功能。仅当我注释掉API调用代码时,我才能测试该操作是否在提交时分派。如果我不评论,则会引发错误。

如何调用模拟API调用而不是实际的API?或如何使用redux-mock-store进行 formik +异步API调用

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.'
      )
    )
  }
}

这是当操作中的API调用部分被注释掉时的console.log

[
       { 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 |       })

1 个答案:

答案 0 :(得分:2)

从2.1.0版本开始,Redux Thunk支持注入自定义参数,您可以将其用于应用程序中使用的api,例如:

<ActionSheet
    ref={o => this.ActionSheet = o}
    options={[
        (
            <TouchableOpacity style={styles.actionTextInline} onPress={() => {

                # ----- change here -------

                this.ActionSheet.hide();
                setTimeout(() => this.openReservationModal(), 1000)

                # ----- change here -------enter code here

            }}>
                <Icon name="receipt" size={24} style={{ color: '#737373' }} />
            </TouchableOpacity>
        ), (
            <TouchableOpacity style={styles.actionTextInline}>
                <Icon name="call" size={24} style={{ color: '#737373' }} />
            </TouchableOpacity>
        ), (
            <TouchableOpacity style={styles.actionTextInline} onPress={() => { alert("Hello friends !!") }}>
                <Icon name="message" size={24} style={{ color: '#737373' }} />
            </TouchableOpacity>
        )
    ]}
    cancelButtonIndex={2}
    destructiveButtonIndex={-1}
    styles={{
        body: {
            flex: 1,
            backgroundColor: '#FFFFFF',
            borderRadius: 10,
            height: 100
        },
        cancelButtonBox: {
            height: 50,
            backgroundColor: '#FFFFFF',
            paddingLeft: 16,
            paddingTop: 16,
            alignItems: 'flex-start'
        },
        buttonBox: {
            height: 50,
            backgroundColor: '#FFFFFF',
            borderRadius: 10,
            paddingLeft: 16,
            paddingTop: 16,
            alignItems: 'flex-start'
        },
    }}
/>

然后在您的thunk acion中删除导入Auth并将其从第三个参数中提取出来:

const store = configureStore({
  reducer: {
    auth: authReducer,
  },
  middleware: [
    getDefaultMiddleware({
      thunk: {
        extraArgument: storeAuth,
      },
    }),
  ],
})

要进行测试,您应该创建模拟存储并将模拟api作为额外参数添加到thunk

return async (dispatch, getState, { storeAuth }) => {
           dispatch(setTempUsername(username))
         await storeAuth.forgotPassword(username)
             .then(() => {
               dispatch(setResettingPassword(true))
             })