我对redux和编程很感兴趣,而且我很难绕过某些单元测试概念。
我在redux中有一些异步操作,涉及调用第三方API(来自“amazon-cognito-identity-js'节点模块”)。
我已将外部API调用包含在promise函数中,并且我从实际的'中调用此函数。行动创造者。因此,对于测试,我只想存根externalAWS()
函数的结果,以便我可以检查是否正在调度正确的操作。
我使用redux-thunk作为我的中间件。
import { AuthenticationDetails,
CognitoUser
} from 'amazon-cognito-identity-js';
export function externalAWS(credentials) {
//This is required for the package
let authenticationDetails = new AuthenticationDetails(credentials);
let cognitoUser = new CognitoUser({
//Construct the object accordingly
})
return new Promise ((resolve, reject) => {
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: result => {
resolve(result);
},
onFailure: error => {
reject(error)
}
})
}
}
export function loginUser(credentials) {
//return function since it's async
return dispatch => {
//Kick off async process
dispatch(requestLogin());
externalAWS(credentials)
.then((result) => {
dispatch(receiveLogin(result.getAccessToken().getJwtToken(), credentials.username))
})
.catch((error) => {
dispatch(failedLogin(error.message, etc))
})
}
}
我还没有任何测试代码,因为我真的不确定如何处理这个问题。所有的例子都涉及模拟HTTP请求,我知道这是 这归结为什么,我应该在我的浏览器中检查HTTP请求并直接模拟它们?
authenticateUser
的第二个参数甚至不是一个普通的回调,而是一个回调作为它的值的对象,这使事情变得更加复杂。
有人可以提供一些建议,说明我对单元测试异步功能的意图是否正确,以及我应该如何处理它?谢谢。
编辑:我在Jest进行测试。
Edit2:请求标头 First POST request, Second POST request
编辑3:拆分该功能,尽我所能隔离外部API并创建一些“易于模拟/存根”的功能。但是仍然遇到如何正确存根这个功能的问题。
答案 0 :(得分:0)
Redux thunk使您能够在启动流程的主要操作的上下文中调度未来的操作。这个主要动作是你的thunk行动创造者。
因此,测试应该关注根据api请求的结果在您的thunk动作创建者中调度的操作。
测试还应该查看哪些参数传递给您的操作创建者,以便您的Reducer可以被告知请求的结果并相应地更新商店。
要开始测试您的thunk action creator,您要测试是否根据登录是否成功来适当地调度这三个操作。
以下是我为您开始使用Nock拦截http请求的一些测试。
<强>测试强>
import nock from 'nock';
const API_URL = 'https://cognito-idp.us-west-2.amazonaws.com/'
const fakeCredentials = {
username: 'fakeUser'
token: '1234'
}
it('dispatches REQUEST_LOGIN and RECEIVE_LOGIN with credentials if the fetch response was successful', () => {
nock(API_URL)
.post( ) // insert post request here e.g - /loginuser
.reply(200, Promise.resolve({"token":"1234", "userName":"fakeUser"}) })
return store.dispatch(loginUser(fakeCredentials))
.then(() => {
const expectedActions = store.getActions();
expect(expectedActions.length).toBe(2);
expect(expectedActions[0]).toEqual({type: 'REQUEST_LOGIN'});
expect(expectedActions[1]).toEqual({type: 'RECEIVE_LOGIN', token: '1234', userName: 'fakeUser'});
})
});
it('dispatches REQUEST_LOGIN and FAILED_LOGIN with err and username if the fetch response was unsuccessful', () => {
nock(API_URL)
.post( ) // insert post request here e.g - /loginuser
.reply(404, Promise.resolve({"error":"404", "userName":"fakeUser"}))
return store.dispatch(loginUser(fakeCredentials))
.then(() => {
const expectedActions = store.getActions();
expect(expectedActions.length).toBe(2);
expect(expectedActions[0]).toEqual({type: 'REQUEST_LOGIN'});
expect(expectedActions[1]).toEqual({type: 'FAILED_LOGIN', err: '404', userName: 'fakeUser'});
})
});
答案 1 :(得分:0)
所以我最终弄明白了。
首先,我必须将()模块放入我的测试文件中(而不是ES6导入)。然后我删除了现在的承诺,因为它增加了一层复杂性并将所有内容组合成一个函数,让我们称之为loginUser()
。它是一个redux异步操作,在调用时调度一个操作,然后根据API调用的结果调度成功或失败操作。请参阅上面的API调用内容。
然后我按如下方式编写了测试:
const CognitoSDK = require('/amazon-cognito-identity-js')
const CognitoUser = CognitoSDK.CognitoUser
//Set up the rest of the test
describe('async actions', (() => {
it('should dispatch ACTION_1 and ACTION_2 on success', (() => {
let CognitoUser.authenticateUser = jest.fn((arg, callback) => {
callback.onSuccess(mockResult)
})
store.dispatch(loginUser(mockData))
expect(store.getActions()).toEqual([{ACTION_1}, {ACTION_2}])
}))
}))
所以基本上一旦需要模块,我在Jest中嘲笑它并做了一个模拟实现,这样我就可以访问回调对象的onSuccess函数了。