Redux Testing - ReferenceError: localStorage is no

2019-04-12 12:14发布

I'm currently having a lot of trouble running tests on my redux actions. The test passes but I get the following error each time it is ran:

ReferenceError: localStorage is not defined

I also got an error before which was:

ReferenceError: fetch is not defined

I fixed this by using isomorphic-fetch. Anyway I am unsure on how I should configure Mocha to run these front end tests. Any help would be much appreciated.


Mocha test command:

mocha -w test/test_helper.js test/*.spec.js

test_helper.js:

require('babel-register')();
var jsdom = require('jsdom').jsdom;

var exposedProperties = ['window', 'navigator', 'document'];

global.document = jsdom('');
global.window = document.defaultView;

Object.keys(document.defaultView).forEach((property) => {
  if (typeof global[property] === 'undefined') {
    exposedProperties.push(property);
    global[property] = document.defaultView[property];
  }
});

global.navigator = {
  userAgent: 'node.js'
};

documentRef = document;

auth.actions.spec.js

import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
import * as actions from '../client/app/actions/auth'
import * as types from '../client/app/constants/ActionTypes'
import nock from 'nock'
import chai from 'chai'
import sinon from 'sinon'

var expect = chai.expect

import { SERVER_API } from './config'

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

describe('auth actions', () => {

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

it('creates LOGIN_REQUEST and LOGINSUCCESS when correct username and password provided', () => {

    nock(SERVER_API)
    .post('/login', {
      username: 'test',
      password: 'password'
    })
    .reply(200, {
      token: 'TOKEN'
    });

    const expectedActions = [
    { 
        type: types.LOGIN_REQUEST,
        isFetching: true,
        isAuthenticated: false,
        creds: {
          username: 'test',
         password: 'password'
        } 
    },
    { 
        type: types.LOGIN_SUCCESS,
        isFetching: false,
        isAuthenticated: true,
        token: 'TOKEN'
    }
    ]

    const INITAL_STATE = {
      isFetching: false,
      isAuthenticated: false
    }
    const store = mockStore(INITAL_STATE)

    return    store.dispatch(actions.loginUser({username:'test',password:'password'}))
       .then(() => {
        expect(store.getActions()).to.deep.equal(expectedActions)
       })
    })
})

auth.js

import { push } from 'react-router-redux'
import 'es6-promise'
import fetch from 'isomorphic-fetch'

import {
  LOGIN_REQUEST, LOGIN_SUCCESS, LOGIN_FAILURE
} from '../constants/ActionTypes.js'

import { SERVER_PORT } from '../constants/config'


function requestLogin(creds) {
  return {
    type: LOGIN_REQUEST,
    isFetching: true,
    isAuthenticated: false,
    creds
  }
}

function receiveLogin(user) {
  return {
    type: LOGIN_SUCCESS,
    isFetching: false,
    isAuthenticated: true,
    token: user.token
  }
}

function loginError(message) {
  return {
    type: LOGIN_FAILURE,
    isFetching: false,
    isAuthenticated: false,
    message
  }
}

export function loginUser(creds) {

  let config = {
    method: 'POST',
    headers: { 'Content-Type':'application/x-www-form-urlencoded' },
    body: `username=${creds.username}&password=${creds.password}`
  }

  return dispatch => {
    dispatch(requestLogin(creds))
    return fetch('http://localhost:'+SERVER_PORT+'/api/login', config)
      .then(response =>
        response.json()
        .then(user => ({ user, response }))
      ).then(({ user, response }) =>  {
        if (!response.ok) {
          dispatch(loginError(user.message))
          return Promise.reject(user)
        }
        else {
          dispatch(receiveLogin(user))
          localStorage.setItem('token', user.token) //offending line
          dispatch(push('foo'))
        }
      }).catch(err => console.log("Error: ", err))
  }
}

Thanks.

1条回答
Bombasti
2楼-- · 2019-04-12 13:01

Pretty straightforward error, you can't use localStorage on your mocha tests because window.localStorage isn't defined. There are two ways to fix this. The more "canon" way would be to move that localStorage call from your action because this is a side-effect which is an anti-pattern in redux actions. Instead you should have middleware that catches this action and sets the localStorage.

By doing that, you already eliminate the issue of testing this action.

If, however, you don't know how to do that and don't think it wise, then you can "fake" localStorage by making a global variable at the top of your mocha test file which creates a fake localStorage. I recommend against this one but it's definitely a solution that could work in your case.

查看更多
登录 后发表回答