4

I follow the basic exmaple of redux.org to test async action

action.js

my code is like this:

import axios from 'axios'

export function getGoodDataStart(){
    return{
        type: "GOOD_DATA_START"
    }
}
export function getGoodDataSuccess(payload){
    console.log('success', payload)
    return {
        type: "GOOD_DATA_SUCCESS",
        payload: payload
    }
}
export function getGoodDataFail(){
    return{
        type: "GOOD_DATA_FAIL"
    }
}
export function getGoodData(){
    return (dispatch) => {
        dispatch( getGoodDataStart() )
        return  axios.get('http://www.google.com/list')
            .then( response => {
                console.log('fake res',response)
                dispatch(getGoodDataSuccess (response) )
            })
            .catch( err => {
                console.log('fake err',err)
            })
    }   
}

test.js

import nock from 'nock'
import React from 'react'
import {expect} from 'chai'
import {getGoodData} from 'registerAction'
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'

const middlewares = [ thunk ]
const mockStore = configureMockStore(middlewares)

describe('Register component', () => {

    it('async action', function () {

        nock('http://www.google.com')
        .get('/list')
        .reply(200,'ok!' )

        const store = mockStore({ 
            myData: '' ,
        })
        const expected = [
            {type: "GOOD_DATA_START"},
            {type: "GOOD_DATA_SUCCESS", payload: 'ok!'}
        ]

        return store.dispatch(getGoodData())
            .then( () => { 
                expect(store.getActions()).to.equal(expected)
            })
    })
})

The problem I have is, nock is not blocking the request, it lets function getGoodData make real request to google.com. What am I doing wrong?

screen shot of the error:

enter image description here

Here is the demo: https://github.com/craigcosmo/react-redux-test

install: npm i

to test: npm run test

open url: http://localhost:5051/webpack-dev-server/

angry kiwi
  • 10,730
  • 26
  • 115
  • 161
  • I think it might be because you have a trailing `/` in your `nock` setup. Instead of `nock('http://example.com/')` do `nock('http://example.com')` :) – Mario Tacke Sep 19 '16 at 23:05
  • removed the `/` and still wouldn't help – angry kiwi Sep 20 '16 at 03:15
  • Are you certain you are hitting/nocking the exact same endpoint? – Mario Tacke Sep 20 '16 at 15:19
  • @MarioTacke I make a demo, updated my question with link to demo – angry kiwi Sep 20 '16 at 17:17
  • In your demo app, you are not running tests separately, but through a webpack-dev-server. Is this intentional? Why not run it with `mocha --compilers js:babel-core/register`? – Mario Tacke Sep 20 '16 at 17:25
  • I tried that, but it did not work, so I settled with webpack-dev-server – angry kiwi Sep 20 '16 at 17:30
  • You have to fix your import references. I just cloned your project and ran it like above. It works and returns nock data. In particular, you have to fix `registerAction.js` => i from '../constant/actionConstant', and {getGoodData} from '../app/action/registerAction'. Remove the other index.js test file. – Mario Tacke Sep 20 '16 at 17:35
  • how do you check if it returns nock data? – angry kiwi Sep 20 '16 at 17:38
  • You are already logging to console. Look at the return data. It says `"ok!"` – Mario Tacke Sep 20 '16 at 17:54
  • I don't see that from my end, can you give me a screen shot? And how exactly you run it? – angry kiwi Sep 20 '16 at 17:57
  • I update a screenshot of what I saw in the question. Make sure you get the latest code – angry kiwi Sep 20 '16 at 18:02
  • It's worth noting here that by using nock in this way your not only testing your own code but you testing axios as well (since axios makes the actual http call). This deviates from typical unit testing best practices. I'd recommend not using nock and mocking axios instead, using spies to make sure it was called correctly. If you're open to that, I'll be happy to provide and example. – Ben Sidelinger Sep 21 '16 at 17:56
  • @BenSidelinger yes i'm open to it. – angry kiwi Sep 22 '16 at 16:19
  • @angry_kiwi I posted an answer that will show you how to both not have to deal with nock and remove axios from the test by mocking axios and asserting that it was called correctly. – Ben Sidelinger Oct 21 '16 at 02:38

3 Answers3

3

Typically when testing an action like this you'll want to remove anything that is not part of your action from the equation. In this case by simply using nock, you're not removing axios from the equation and are actually adding unnecessary complexity. By mocking axios with a spy, you avoid making the network call and you also avoid calling axios at all. This allows you to simply assert that axios is called with the correct parameters. The spy can return a promise that allows testing all the promise handling and subsequent action calls. In order to demonstrate this, I needed to add a library that provides spies, so I opted to add 'expect' for both assertions and spies, but you could easily do the same thing with sinon if you want to stick with chai.

Here's an example where you don't need nock at all and you just mock axios with a spy:

import React from 'react'
import * as registerAction from 'registerAction'
import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
import expect from 'expect'

const middlewares = [ thunk ]
const mockStore = configureMockStore(middlewares)

// set up to mock axios methods
import axios from 'axios'
const _get = axios.get

const fakePayload = { foo: 'bar' };
describe('Register component', () => {
    beforeEach(() => {
        // replace the .get method temporarily with a spy
        axios.get = expect.createSpy().andReturn(Promise.resolve(fakePayload));
    })

    afterEach(() => {
        // restore the get method with our saved const
        axios.get = _get;
    })

    it('async action', function () {
        const store = mockStore({
            myData: '' ,
        })
        const expected = [
            {type: "GOOD_DATA_START"},
            {type: "GOOD_DATA_SUCCESS", payload: fakePayload}
        ]

        return store.dispatch(registerAction.getGoodData())
            .then( () => {
                expect(store.getActions()).toEqual(expected)
                expect(axios.get).toHaveBeenCalled()
                expect(axios.get).toHaveBeenCalledWith('http://www.google.com/list')
            })
    })
})
Ben Sidelinger
  • 1,309
  • 7
  • 12
2

read https://github.com/node-nock/nock/issues/150

Your tests are doing great on console-

add this two script run on your package.json

    "itest": "mocha --compilers js:babel-register -R spec  \"test/*.test.js\"",
    "itest:watch": "npm run itest -- --watch"
Community
  • 1
  • 1
Fazal Rasel
  • 4,446
  • 2
  • 20
  • 31
1

You might need something like this

beforeEach(() => {
    nock.disableNetConnect();
});

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

Enable/Disable real HTTP Request

yjcxy12
  • 1,841
  • 1
  • 14
  • 13