Javascript 如何使用Redux Saga测试API请求失败?
我正在尝试测试我的传奇故事可能发生的每一个场景,但我无法做出我想要的行为。 这很简单,我有一个HTTP请求(登录),我想通过模拟我的API方法来测试成功和失败案例 但是,Javascript 如何使用Redux Saga测试API请求失败?,javascript,reactjs,generator,redux,redux-saga,Javascript,Reactjs,Generator,Redux,Redux Saga,我正在尝试测试我的传奇故事可能发生的每一个场景,但我无法做出我想要的行为。 这很简单,我有一个HTTP请求(登录),我想通过模拟我的API方法来测试成功和失败案例 但是,调用效果似乎没有触发我的api函数,我还不知道它是如何工作的,但我想中间件负责调用函数,因为我没有在测试中查看存储,所以我无法得到结果 所以我的问题是,当您需要在异步调用旁边分派不同的操作(通常是成功或失败)时,如何测试您的传奇 我寻找了一个例子,我发现了成功和失败的传奇故事,但失败案例从未被测试过,例如在购物车的例子中 SAG
调用效果
似乎没有触发我的api函数,我还不知道它是如何工作的,但我想中间件负责调用函数,因为我没有在测试中查看存储,所以我无法得到结果
所以我的问题是,当您需要在异步调用旁边分派不同的操作(通常是成功或失败)时,如何测试您的传奇
我寻找了一个例子,我发现了成功和失败的传奇故事,但失败案例从未被测试过,例如在购物车的例子中
SAGA.JS
export function* login(action) {
try {
const user = yield call(api.login, action);
return yield put(actions.loginSuccess(user));
} catch(e) {
yield put(actions.loginFail(e));
}
}
export default function* rootAuthenticationSagas() {
yield* takeLatest(LOGIN, login);
}
TEST.JS
describe('login', () => {
context('When it fails', () => {
before('Stub the api', () => {
sinon.stub(api, 'login', () => {
// IT NEVER COMES HERE !
return Promise.reject({ error: 'user not found' });
});
});
it('should return a LOGIN_FAIL action', () => {
const action = {
payload: {
name: 'toto',
password: '123456'
}
};
const generator = login(action);
// THE CALL YIELD
generator.next();
const expectedResult = put({ type: 'LOGIN_FAIL', payload: { error: 'user not found' } });
expect(generator.next().value).to.be.eql(expectedResult); // FAIL BECAUSE I GET A LOGIN_SUCCESS INSTEAD OF A FAIL ONE
});
});
});
正确-据我所知,Redux Saga的全部要点是,您的Saga函数使用Saga API返回描述动作的对象,然后中间件稍后查看这些对象以实际执行行为。因此,saga中的
yield调用(myapiffunction,“/someEndpoint”,arg1,arg2)
语句可能返回一个概念上看起来像{effectType:call,function:myapiffunction,params:[arg1,arg2]}
您可以检查redux saga源代码以查看这些声明性对象的实际外观,并创建一个匹配的对象以在测试中进行比较,或者使用API函数本身来创建对象(我认为redux saga在其测试代码中所做的就是这样)。是正确的。中间件执行这些指令。但这会使您的生活更轻松:在测试中,您可以提供任何您想要的作为next()
的参数,并且生成器函数将作为yield
的结果接收它。这正是saga中间件所做的(除了它实际上激发了一个请求,而不是给你一个虚假的响应)
要使yield
获得任意值,请将其传递给next()
。要使其“接收”错误,请将其传递给throw()
。在您的示例中:
it('should return a LOGIN_FAIL action', () => {
const action = {
payload: {
name: 'toto',
password: '123456'
}
};
const generator = login(action);
// Check that Saga asks to call the API
expect(
generator.next().value
).to.be.eql(
call(api.login, action)
);
// Note that *no actual request was made*!
// We are just checking that the sequence of effects matches our expectations.
// Check that Saga reacts correctly to the failure
expect(
generator.throw({
error: 'user not found'
}).value
).to.be.eql(
put({
type: 'LOGIN_FAIL',
payload: { error: 'user not found' }
})
);
});
您还可能希望使用助手库来测试您的saga,例如 免责声明:我编写此库是为了解决完全相同的问题 此库将使您的测试看起来像任何其他(同步)测试,这比手动调用
generator.next()
容易得多
以您的示例为例,您可以编写如下测试:
(它使用Jest语法,但本质上与Mocha相同,它完全不依赖于测试库)
更多示例(使用Jest、Mocha和AVA)
import sagaHelper from 'redux-saga-testing';
import { call, put } from 'redux-saga/effects';
import actions from './my-actions';
import api from './your-api';
// Your example
export function* login(action) {
try {
const user = yield call(api.login, action);
return yield put(actions.loginSuccess(user));
} catch(e) {
yield put(actions.loginFail(e.message)); // Just changed that from "e" to "e.message"
}
}
describe('When testing a Saga that throws an error', () => {
const it = sagaHelper(login({ type: 'LOGIN', payload: 'Ludo'}));
it('should have called the API first, which will throw an exception', result => {
expect(result).toEqual(call(api, { type: 'LOGIN', payload: 'Ludo'}));
return new Error('Something went wrong');
});
it('and then trigger an error action with the error message', result => {
expect(result).toEqual(put(actions.loginFail('Something went wrong')));
});
});
describe('When testing a Saga and it works fine', () => {
const it = sagaHelper(login({ type: 'LOGIN', payload: 'Ludo'}));
it('should have called the API first, which will return some data', result => {
expect(result).toEqual(call(api, { type: 'LOGIN', payload: 'Ludo'}));
return { username: 'Ludo', email: 'ludo@ludo.com' };
});
it('and then call the success action with the data returned by the API', result => {
expect(result).toEqual(put(actions.loginSuccess({ username: 'Ludo', email: 'ludo@ludo.com' })));
});
});