Mock inner axios.create()

2019-01-20 04:17发布

问题:

I'm using jest and axios-mock-adapter to test axios API calls in redux async action creators.

I can't make them work when I'm using a axios instance that was created with axios.create() as such:

import axios from 'axios';

const { REACT_APP_BASE_URL } = process.env;

export const ajax = axios.create({
  baseURL: REACT_APP_BASE_URL,
});

which I would consume it in my async action creator like:

import { ajax } from '../../api/Ajax'

export function reportGet(data) {
  return async (dispatch, getState) => {
    dispatch({ type: REQUEST_TRANSACTION_DATA })

    try {
      const result = await ajax.post(
         END_POINT_MERCHANT_TRANSACTIONS_GET,
         data,
      )
      dispatch({ type: RECEIVE_TRANSACTION_DATA, data: result.data })
      return result.data
    } catch (e) {
      throw new Error(e);
    }
  }
}

Here is my test file:

import {
  reportGet,
  REQUEST_TRANSACTION_DATA,
  RECEIVE_TRANSACTION_DATA,
} from '../redux/TransactionRedux'
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
import { END_POINT_MERCHANT_TRANSACTIONS_GET } from 'src/utils/apiHandler'
import axios from 'axios'
import MockAdapter from 'axios-mock-adapter'

const middlewares = [thunk]
const mockStore = configureMockStore(middlewares)
const store = mockStore({ transactions: {} })

test('get report data', async () => {
  let mock = new MockAdapter(axios)

  const mockData = {
    totalSalesAmount: 0
  }

  mock.onPost(END_POINT_MERCHANT_TRANSACTIONS_GET).reply(200, mockData)

  const expectedActions = [
    { type: REQUEST_TRANSACTION_DATA },
    { type: RECEIVE_TRANSACTION_DATA, data: mockData },
  ]

  await store.dispatch(reportGet())
  expect(store.getActions()).toEqual(expectedActions)
})

And I only get one action Received: [{"type": "REQUEST_TRANSACTION_DATA"}] because there was an error with the ajax.post.

I have tried many ways to mock the axios.create to no avail without really knowing what I'm doing..Any Help is appreciated.

回答1:

OK I got it. Here is how I fixed it! I ended up doing without any mocking libraries for axios!

Create a mock for axios in src/__mocks__:

// src/__mocks__/axios.ts

const mockAxios = jest.genMockFromModule('axios')

// this is the key to fix the axios.create() undefined error!
mockAxios.create = jest.fn(() => mockAxios)

export default mockAxios

Then in your test file, the gist would look like:

import mockAxios from 'axios'
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'

// for some reason i need this to fix reducer keys undefined errors..
jest.mock('../../store/rootStore.ts')

// you need the 'async'!
test('Retrieve transaction data based on a date range', async () => {
  const middlewares = [thunk]
  const mockStore = configureMockStore(middlewares)
  const store = mockStore()

  const mockData = {
    'data': 123
  }

  /** 
   *  SETUP
   *  This is where you override the 'post' method of your mocked axios and return
   *  mocked data in an appropriate data structure-- {data: YOUR_DATA} -- which
   *  mirrors the actual API call, in this case, the 'reportGet'
   */
  mockAxios.post.mockImplementationOnce(() =>
    Promise.resolve({ data: mockData }),
  )

  const expectedActions = [
    { type: REQUEST_TRANSACTION_DATA },
    { type: RECEIVE_TRANSACTION_DATA, data: mockData },
  ]

  // work
  await store.dispatch(reportGet())

  // assertions / expects
  expect(store.getActions()).toEqual(expectedActions)
  expect(mockAxios.post).toHaveBeenCalledTimes(1)
})


回答2:

In your mockAdapter, you're mocking the wrong instance. You should have mocked ajax instead. like this, const mock = MockAdapter(ajax) This is because you are now not mocking the axios instance but rather the ajax because it's the one you're using to send the request, ie, you created an axios instance called ajax when you did export const ajax = axios.create...so since you're doing const result = await ajax.post in your code, its that ajax instance of axios that should be mocked, not axios in that case.