0

原始问题

我正在按照Redux 文档中为异步操作创建者编写测试的示例进行操作。我尽可能地遵循这个例子,但我无法让测试工作。我收到以下错误消息:

   TypeError: Cannot read property 'then' of undefined
   (node:789) UnhandledPromiseRejectionWarning: Unhandled promise rejection 
   (rejection id: 28): TypeError: Cannot read property 'data' of undefined

这是我的动作创建者和测试的代码:

动作/index.js

import axios from 'axios';
import { browserHistory } from 'react-router';
import { AUTH_USER, AUTH_ERROR, RESET_AUTH_ERROR } from './types';

const API_HOST = process.env.NODE_ENV == 'production'
                ? http://production-server
                : 'http://localhost:3090';

export function activateUser(token) {
  return function(dispatch) {
    axios.put(`${API_HOST}/activations/${token}`)
      .then(response => {
        dispatch({ type: AUTH_USER });

        localStorage.setItem('token', response.data.token);
      })
      .catch(error => {
        dispatch(authError(error.response.data.error));
      });
  }
}

export function authError(error) {
  return {
    type: AUTH_ERROR,
    payload: error
  }
}

确认测试.js

import configureMockStore from 'redux-mock-store'; 
import thunk from 'redux-thunk';
import * as actions from '../../src/actions';
import { AUTH_USER, AUTH_ERROR, RESET_AUTH_ERROR } from 
'../../src/actions/types';
import nock from 'nock';
import { expect } from 'chai';

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

describe('Confirmation_Token action creator', () => {
  afterEach(() => {
    nock.cleanAll()
  });

  it('dispatches AUTH_USER', (done) => {
    nock('http://localhost:3090')
    .put('/activations/123456')
    .reply(200, {
        token: 7891011
    });

    const expectedActions = { type: AUTH_USER };

    const store = mockStore({});

    return store.dispatch(actions.activateUser(123456))
     .then(() => { // return of async actions
       expect(store.getActions()).toEqual(expectedActions);
       done();
     });
  });
});

更新的问题

我已经部分(尽管不完全)弄清楚了这一点。我通过在调用return前面添加一个语句axios并注释掉localstorage.setItem调用来实现这一点。

我还将分配给的对象转换expectedActions为一个数组,并将我的断言从 更改toEqualto.deep.equal。这是修改后的代码:

动作/index.js

export function activateUser(token) {
  return function(dispatch) { // added return statement
    return axios.put(`${API_HOST}/activations/${token}`)
      .then(response => {
        dispatch({ type: AUTH_USER });
        // localStorage.setItem('token', response.data.token); Had to comment out local storage
      })
      .catch(error => {
        dispatch(authError(error.response.data.error));
      });
  }
}

确认测试.js

describe('ConfirmationToken action creator', () => {
  afterEach(() => {
     nock.cleanAll()
  });

  it('dispatches AUTH_USER', (done) => {
    nock('http://localhost:3090')
    .put('/activations/123456')
    .reply(200, {
        token: 7891011
     });

    const expectedActions = [{ type: AUTH_USER }];

    const store = mockStore({});

    return store.dispatch(actions.activateUser(123456))
     .then(() => { // return of async actions
       expect(store.getActions()).to.deep.equal(expectedActions);
       done();
     });
   });
 });

但现在我无法在localStorage.setItem不产生此错误消息的情况下进行测试:

Error: timeout of 2000ms exceeded. Ensure the done() callback is being called 
in this test.

这是因为我需要模拟localStorage.setItem吗?还是有一个我想念的更简单的解决方案?

4

1 回答 1

0

我想出了解决方案。它涉及我在更新的问题中所做的更改以及在我的 test_helper.js 文件中添加 localStorage 的模拟。由于网上似乎有很多关于此的问题,我想也许我的解决方案可以帮助某人。

test_helper.js

import jsdom from 'jsdom';

global.localStorage = storageMock();

global.document = jsdom.jsdom('<!doctype html><html><body></body></html>');
global.window = global.document.defaultView;
global.navigator = global.window.navigator;

global.window.localStorage = global.localStorage;

// localStorage mock

function storageMock() {
    var storage = {};

    return {
      setItem: function(key, value) {
        storage[key] = value || '';
      },
      getItem: function(key) {
        return key in storage ? storage[key] : null;
      },
      removeItem: function(key) {
        delete storage[key];
      }
    };
  }

action.index.js

export function activateUser(token) {
  return function(dispatch) {
    return axios.put(`${API_HOST}/activations/${token}`)
      .then(response => {
        dispatch({ type: AUTH_USER });
        localStorage.setItem('token', response.data.token);
      })
      .catch(error => {
        dispatch(authError(error.response.data.error));
      });
  }
}

确认测试.js

describe('Confirmation action creator', () => {
  afterEach(() => {
    nock.cleanAll()
  });

  it('dispatches AUTH_USER and stores token in localStorage', (done) => {
    nock('http://localhost:3090')
    .put('/activations/123456')
    .reply(200, {
        token: '7891011'
    });

    const expectedActions = [{ type: AUTH_USER }];

    const store = mockStore({});

    return store.dispatch(actions.activateUser(123456))
     .then(() => { // return of async actions
       expect(store.getActions()).to.deep.equal(expectedActions);
       expect(localStorage.getItem('token')).to.equal('7891011');
       done();
    });
  });
});
于 2017-04-12T01:27:03.837 回答