Nock is not intercepting API call in Redux test

2019-04-24 10:55发布

问题:

I'm trying to test an api call in a redux app. The code pretty much follows the pattern outlined in the Async Action Creators section of the redux docs:

http://redux.js.org/docs/recipes/WritingTests.html

The gist of it is that you use redux-mock-store to record and assert against any actions that are triggered.

This is the whole test, using nock to mock the api call:

import React from 'React'
import ReactDOM from 'react-dom'
import expect from 'expect';
import expectJSX from 'expect-jsx';
import TestUtils from 'react-addons-test-utils'
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
import nock from 'nock'
expect.extend(expectJSX);

import * as types from '../../constants/Actions'

describe('Async Search Actions', () => {
    const thunkMiddleware = [ thunk ];
     /* use redux-mock-store here */
    const mockStore = configureMockStore(thunkMiddleware);


    describe('The fetchArtistData action creator should', () => {

            afterEach(() => {
                nock.cleanAll()
            })

        it('Should fire off a ARTIST action when fetch is done', (done) => {
            nock('http://ws.audioscrobbler.com')
                .get('/2.0/')
                .query({method: 'artist.search', artist: 'ho', api_key: 'abc123', format: 'json', limit: 5})
                .reply(200, 
                      {
                        fake: true
                      }
                   )



            const expectedActions = [
                { type: types.ARTIST, artists: {
                        fake: true
                    } 
                }
            ];

            let store = mockStore([], expectedActions, done);
            store.dispatch(fetchArtist('ho'))

        });

    });

});

But it seems that the real lastFm api is called when the test is run...real data is returned from lastFm rather than the fake nock response.

This is the action creator itself:

export function fetchArtist(search) {
    return dispatch => {
        return fetch(`http://ws.audioscrobbler.com/2.0/?method=artist.search&artist=${search}&api_key=abc123&format=json&limit=5`)
            .then(handleErrors)
            .then(response => response.json())
            .then(json => { dispatch(ArtistData(searchTerm, json)) })
            .catch(handleServerErrors)
    }
}

The assertion fails because the live lastFM response is not the same as the response I'm expecting as per the expectedActions object..

I've tried assigning the nock to a variable and log it out.The log shows this:

Nock seems to be adding port 80 to the url, not sure if this is causing the actual API to not be mocked:

    keyedInterceptors: Object{GET http://ws.audioscrobbler.com:80/2.0/?
method=artist.search&artist=john&api_key=abc123&format=json&limit=5

Any ideas what's wrong here?

回答1:

In order to use nock you must run your tests in node (using Jest or mocha), nock overrides node http behavior and for that reason it only works in node and not in browsers (like PhantomJS).

For example the link you pointed out is using Jest and first lines there are explicit about the node environment. Therefore, nock will work as a charm. http://redux.js.org/docs/recipes/WritingTests.html

Setting Up

We recommend Jest as the testing engine. Note that it runs in a Node environment, so you won't have access to the DOM.

As I see you can:

  • run your tests in a node environment
  • or use a different library to mock like fetch-mock


回答2:

You need to pass just base URL to the main nock function and separate the URL path portion out into the .get() method.

nock('http://ws.audioscrobbler.com')
  .get('/2.0/')
  .query({
    method: 'artist.search',
    artist: 'bob',
    api_key: 'somekey123',
    format: 'json',
    limit: '5'
  })
  .reply(200, {fake: true})

I was able to get a fake response with the above code.