Teun Steenbekkers Teun Steenbekkers - 1 month ago 31
React JSX Question

Returned JSON from fetch requests differs in test and dev



I hope my question title is comprehensible enough. I could't really think of a better way to phrase it and the character count restrictions busted my balls.

The problem I'm having happens in a project I'm working on that uses React, Redux and packages like react-router-redux and redux-auth-wrapper.

For testing I'm using Karma test runner with Enzyme, Sinon and Chai. Within the tests I'm using fetchMock to mock any requests to the API I'm using. I bundle everything using Webpack.

The issue I'm facing is that the code I wrote (modified this starter template rather rigorously) works, but the tests I wrote for them don't produce correct results. The bit that's acting weird is when I dispatch an action to fetch a token from our authentication API.

The Redux action I'm dispatching is the following one:

export function fetchToken(data) {
return function(dispatch) {
dispatch(requestTokenCreate(data))

// Let's fetch this user a token, shall we?
var headers = new Headers()
headers.append("Content-Type", "application/json")
headers.append("Accept", "application/json")

let requestParams = {
method: 'POST',
headers: headers,
mode: 'cors',
cache: 'default',
body: JSON.stringify({
subdomain: data.subdomain,
login: data.username,
password: data.password
})
}
data = {
subdomain: data.subdomain,
username: data.username
}
var serverUrl = (__DEV__ || __TEST__) ? "http://subdomain.easyposonline.dev:3000/api/v1" : "https://subdomain.easyposonline.nl/api/v1"
serverUrl = serverUrl.replace("subdomain", data.subdomain)
return fetch(serverUrl + "/auth_tokens/create", requestParams)
.then(response => response.json())
.then(json => {
return dispatch(receiveTokenCreate(json, data))
}
)
}
}


The bit relevant to my question is this:

return fetch(serverUrl + "/auth_tokens/create", requestParams)
.then(response => response.json())
.then(json => {
return dispatch(receiveTokenCreate(json, data))
}
)


When I dispatch this action in my development code, it works just fine! This returns the following response:

{success: true, token: "a3PC6y6nx1f63o2b13YWz7LYUHdidYui"}


When the action is dispatched from the test in question, though, I get back the following response:

{type: 'RECEIVE_USER_TOKEN_CREATE', json: Object{success: true, token: 'qicXyn6BhYhyoBuA_SFoZtTDN'}, payload: Object{subdomain: 'test-teun', username: 'teun@easypos.nl', token: 'qicXyn6BhYhyoBuA_SFoZtTDN'}}


So instead of returning the proper response, the code returns what seems to be an entire Redux action. This leads to my test failing because I end up with a bit of a recursion in the code I'm testing:

{type: 'RECEIVE_USER_TOKEN_CREATE', response: Object{type: 'RECEIVE_USER_TOKEN_CREATE', json: Object{success: ..., token: ...}, payload: Object{subdomain: ..., username: ..., token: ...}}, payload: Object{subdomain: 'test-teun', username: 'teun@easypos.nl'}}


I'm at a loss here. Probably I'm overlooking something astonishingly simple, but been looking at this for a few hours now and starting to lose my frickin' mind!

The test file I wrote is the following, maybe I'm setting up a test the wrong way:

import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
// import nock from 'nock'
import 'isomorphic-fetch'
import fetchMock from 'fetch-mock'

import SignInView from 'routes/SignIn'
import * as constants from 'constants'
import { fetchToken, destroyToken } from 'actions/user'
import _ from 'underscore'

describe('(Route) SignIn', () => {
let _route = SignInView({})

// Auth token request
const authTokenCreateSuccessfulRequest = {
subdomain: 'test-teun',
username: 'teun@easypos.nl',
password: 'psv4teun'
}
const authTokenCreateFailedRequest = {
subdomain: 'test-teun',
username: 'teun@easypos.nl',
password: 'teun4psv'
}
const authTokenCreateSuccessfulResponse = {
type: constants.RECEIVE_USER_TOKEN_CREATE,
json: {
success: true,
token: 'qicXyn6BhYhyoBuA_SFoZtTDN'
},
payload: {
subdomain: 'test-teun',
username: 'teun@easypos.nl',
token: 'qicXyn6BhYhyoBuA_SFoZtTDN'
}
}
const authTokenCreateFailedResponse = {
type: constants.RECEIVE_USER_TOKEN_CREATE,
json: {
success: false,
reason: "Ongeldige gebruikersnaam of wachtwoord!"
},
payload: {
subdomain: 'test-teun',
username: 'teun@easypos.nl'
}
}

beforeEach(() => {
fetchMock.mock('http://test-teun.easyposonline.dev:3000/api/v1/auth_tokens/create', authTokenCreateSuccessfulResponse)
})

afterEach(() => {
fetchMock.restore()
})

it('Should return a route configuration object', () => {
expect(typeof _route).to.equal('object')
})

it('Should define a route component', () => {
expect(_route.path).to.equal('sign_in')
})

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

it('Should trigger RECEIVE_USER_TOKEN_CREATE action containing a token on a successful login attempt', () => {
//fetchMock.mock('http://test-teun.easyposonline.dev:3000/api/v1/auth_tokens/create', authTokenCreateSuccessfulResponse)
const expectedActions = [
{
type: constants.REQUEST_USER_TOKEN_CREATE,
payload: authTokenCreateSuccessfulRequest
},
authTokenCreateSuccessfulResponse
]
const store = mockStore({ user: { isFetching: false } })

return store.dispatch(fetchToken(authTokenCreateSuccessfulRequest))
.then((data) => {
console.log(store.getActions()[1])
// console.log(expectedActions[1])
expect(_.isEqual(store.getActions(), expectedActions)).to.equal(true)
})
})
})


Another thing I might note is that I had this test working yesterday but I modified the structure of my Redux state since then and seem to have broken something along the way... :( Looking back in my Git history did not help me with this annoying thing.

I hope someone can point me in the right direction here, it'd really help me and my peace of mind. Thanks a bunch in advance!

Answer

Sigh... I discovered my dumb error. fetchMock.mock('http://test-teun.easyposonline.dev:3000/api/‌​v1/auth_tokens/creat‌​e', authTokenCreateSuccessfulResponse) should be fetchMock.mock('http://test-teun.easyposonline.dev:3000/api/‌​v1/auth_tokens/creat‌​e', authTokenCreateSuccessfulResponse.payload)