Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何使用Redux Saga测试API请求失败?_Javascript_Reactjs_Generator_Redux_Redux Saga - Fatal编程技术网

Javascript 如何使用Redux Saga测试API请求失败?

Javascript 如何使用Redux Saga测试API请求失败?,javascript,reactjs,generator,redux,redux-saga,Javascript,Reactjs,Generator,Redux,Redux Saga,我正在尝试测试我的传奇故事可能发生的每一个场景,但我无法做出我想要的行为。 这很简单,我有一个HTTP请求(登录),我想通过模拟我的API方法来测试成功和失败案例 但是,调用效果似乎没有触发我的api函数,我还不知道它是如何工作的,但我想中间件负责调用函数,因为我没有在测试中查看存储,所以我无法得到结果 所以我的问题是,当您需要在异步调用旁边分派不同的操作(通常是成功或失败)时,如何测试您的传奇 我寻找了一个例子,我发现了成功和失败的传奇故事,但失败案例从未被测试过,例如在购物车的例子中 SAG

我正在尝试测试我的传奇故事可能发生的每一个场景,但我无法做出我想要的行为。 这很简单,我有一个HTTP请求(登录),我想通过模拟我的API方法来测试成功和失败案例

但是,
调用效果
似乎没有触发我的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' })));
    });
});