Redux测试 - 引用错误:localStorage未定义

4

我在运行redux操作的测试时遇到了很多麻烦。尽管测试通过,但每次运行测试都会出现以下错误:

ReferenceError: localStorage is not defined

我之前也遇到过一个错误,错误信息如下:

ReferenceError: fetch is not defined

我用同构-fetch解决了这个问题。不过我不确定应该如何配置Mocha来运行这些前端测试。非常感谢帮助。


Mocha测试命令:

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))
  }
}

谢谢。

感谢。

1个回答

11
非常简单的错误,您不能在mocha测试中使用localStorage,因为window.localStorage未定义。有两种方法可以解决这个问题。 更“规范”的方法是将localStorage调用从您的操作中移除,因为这是redux操作中的副作用,是反模式。而应该有一个中间件来捕获此操作并设置localStorage
通过这样做,你可以消除测试此操作时遇到的问题。
然而,如果您不知道如何做到这一点,或者认为不明智,您可以在您的mocha测试文件顶部创建一个全局变量来创建一个虚假的localStorage,虽然我不建议这样做,但这确实是一个可能适用于您情况的解决方案。

感谢您的回复和建议。我原本也是这么想的,但不确定是否可以使用test_helper来伪造localstorage。我一定会尝试使用中间件。谢谢。 - edcarr
@ZekeDroid 如果这样的话,还需要在localStorage全局变量中创建setItem方法,对吗? - Emo
除了其他方法,是的。 - ZekeDroid
@ZekeDroid 我是React Redux的新手,并且正在使用Redux Sagas。我不太确定您所说的中间件是什么意思。我将我的localStorage调用移动到了一个全局函数中,在saga中调用这个函数,这是否可以被视为一种干净的解决方案? - Matt
不行,因为你的 saga 现在依赖于 localStorage。我建议你阅读一下关于 Redux 中间件的内容:https://redux.js.org/advanced/middleware。 - ZekeDroid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接