我们如何在Redux Async Actions中模拟fetch?

时间:2015-12-29 19:05:02

标签: javascript unit-testing reactjs redux redux-thunk

在Redux的编写测试部分http://rackt.org/redux/docs/recipes/WritingTests.html中,如果store.dispatch字面上调用的是actions.fetchTodos,那么store.dispatch(actions.fetchTodos())如何不调用fetch方法?

it('creates FETCH_TODOS_SUCCESS when fetching todos has been done', (done) => {
    nock('http://example.com/')
      .get('/todos')
      .reply(200, { todos: ['do something'] })

const expectedActions = [
  { type: types.FETCH_TODOS_REQUEST },
  { type: types.FETCH_TODOS_SUCCESS, body: { todos: ['do something']  } }
]
const store = mockStore({ todos: [] }, expectedActions, done)
store.dispatch(actions.fetchTodos())


})

每当我尝试运行与此类似的东西时,我一直都没有定义获取。即使我使用nock。所以我必须监视我的行动,不接受fetch的调用。

这是我的单元测试:

it('should request a password reset, and then return success on 200', (done) => {
        nock('http://localhost:8080/')
        .post('/password-reset-requests')
        .reply(200);


var email = "test@email.com";
        const expectedActions=[
            {type: REQUEST_ADD_PASSWORD_RESET_REQUEST},
            {type: REQUEST_ADD_PASSWORD_RESET_REQUEST_SUCCESS}
        ];
        const store = mockStore({}, expectedActions, done);
        store.dispatch(Actions.addPasswordResetRequest());

这是行动:

export default function addPasswordResetRequest(email){
    return dispatch => {
        dispatch(requestAddPasswordResetRequest(email));
        return addPasswordResetRequestAPI(email)
            .then(() =>{
                dispatch(requestAddPasswordResetRequestSuccess());
            })
            .catch((error) => {
            dispatch(requestAddPasswordResetRequestFailure(error));
        });
    };
}

和调用fetch的函数:

export const addPasswordResetRequestAPI = (email) => {
    return fetch(
        SETTINGS.API_ROOT + '/password-reset-requests',
        {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                email: email,
                code: NC_SETTINGS.GROUP.code
            })
        }
    )
        .then(handleResponse);
};

我不确定我正在做的方式是否足以仅仅测试操作,但是我确实遇到了store.dispatch只返回expectedActions的第一个元素的问题,而且它没有&# 39; t等于我在间谍addPasswordResetRequest中提供的列表。以下包括间谍行动。

it('should request a password reset, and then return success on 200', (done) => {
        nock('http://localhost:8080/')
        .post('/password-reset-requests')
        .reply(200);
        Actions.addPasswordResetRequest = spy(() => {
    return ([
            {type: REQUEST_ADD_PASSWORD_RESET_REQUEST},
            {type: REQUEST_ADD_PASSWORD_RESET_REQUEST_SUCCESS}
        ]
        );
});
        var email = "test@email.com";
        const expectedActions=[
            {type: REQUEST_ADD_PASSWORD_RESET_REQUEST},
            {type: REQUEST_ADD_PASSWORD_RESET_REQUEST_SUCCESS}
        ];

        const store = mockStore({}, expectedActions, done);
        store.dispatch(Actions.addPasswordResetRequest());

1 个答案:

答案 0 :(得分:2)

动作" addPasswordResetRequest"不是每个人说的动作。

它是一个包含3个子动作的复合动作

startAction   =requestAddPasswordResetRequest,
successAction =requestAddPasswordResetRequestSuccess 
failAction    =requestAddPasswordResetRequestFailure

我通常会分别测试每个动作。所以我会有像

这样的东西
describe("requestAddPasswordResetRequest", () => {
     it("shows the loading spinner or whatever", ...);
     it("does some other state change maybe", ...);
});
describe("requestAddPasswordResetRequestSuccess", () => {
     it("hides the loading spinner or whatever", ...);
     it("changes the password state or something", ...);
});
describe("requestAddPasswordResetRequestFailure", () => {
     it("hides the loading spinner or whatever", ...);
     it("shows the error somehow", ...);
});

//each test would be something like
it("changes the password state or something", ()=>{
    const action = requestAddPasswordResetRequestSuccess({
          some : "payload from the server"
    });
    const newState = myReducer({ state : "somestate" }, action);
    expect(newState).to.be.eql("expected result for that action"); 
});

注意在测试中我不需要商店或任何异步逻辑。这就是redux的美丽(以及一般的功能),它很简单:)

在此之后我会对整个事情进行单独的测试,并确保复合动作调出正确的简单动作,我将模拟所有内容(包括商店和" fetch"事物,因为我只是想测试动作以正确的顺序被触发)。

如果以正确的顺序发送操作并且每个操作分开工作,我将非常确信该事情按预期工作。

希望这会有所帮助。