如何在 redux-saga 中测试异步 axios 请求?

13t*_*les 5 redux-saga react-redux axios axios-mock-adapter

我有一个以下形式的传奇:

export function* apiRequest(apiBaseUrl, action) {
  const axiosInst = getAxiosInst(apiBaseUrl);

  try {
    if (!action.serviceName) {
      throw new Error("No service name provided");
    }
    const response = yield call( axiosInst.get, `/${action.serviceName}/foo-api` );

    const data = response.data;

    let resultAction;

    switch (response.status) {
      case 404:
        resultAction = INVALID_ENTITY_REQUESTED;
        break;
      case 200:
        ...
      default:
        throw new Error("Invalid response from server.");
    }
    yield put({ type: resultAction, data });
  } catch (err) {
    yield put({
      type: ERROR,
      error: err.message || "There was an unknown error."
    });
  }
}

export function* watchApiRequest(apiBaseUrl) {
  const boundApiRequest = apiRequest.bind(null, apiBaseUrl);

  yield takeEvery(API_CALL, boundApiRequest);
}
Run Code Online (Sandbox Code Playgroud)

以及如下测试:

import { apiRequest } from "../services/apiRequest.js";
import MockAdapter from "axios-mock-adapter";
import { default as axios } from "axios";
import { put } from "redux-saga/effects";
import {
  API_CALL,
  API_SUCCESS
} from "../common/actions.js";

describe("Saga that will run on every api call event", () => {
  const mock = new MockAdapter(axios);

  afterEach(() => {
    mock.reset();
  });

  it("should dispatch the correct event when an API request succeeds", () => {
    mock.onGet().reply(200, { foo: "bar" });

    const generator = apiRequest("", {
      type: API_CALL,
      serviceName: "test"
    });

    generator.next();

    expect(generator.next().value).toMatchObject(
      put({
        type: API_SUCCESS,
        data: { foo: "bar" }
      })
    );
  });
});
Run Code Online (Sandbox Code Playgroud)

这是行不通的。我的测试失败,结果如下:

Expected value to match object:
      {"@@redux-saga/IO": true, "PUT": {"action": {"type": "API_SUCCESS"}, "channel": null}}
    Received:
      {"@@redux-saga/IO": true, "PUT": {"action": {"error": "Cannot read property 'data' of undefined", "type": "ERROR"}, "channel": null}}
Run Code Online (Sandbox Code Playgroud)

该代码在实际使用中似乎运行良好,但是当我尝试以这种方式测试它时,似乎通过 Axios 对 API 的异步调用的承诺无法解析。我已经搜索了一些关于在 Axios 中测试 API 调用的指南,并看到了一些建议,而不是使用 Axios 模拟适配器模拟 API 响应,我应该通过generator.next({status: 200, data: { foo: "bar" })expect(...)调用来向生成器函数提供响应,但这并不似乎也不起作用。

我发现有关测试的 redux-saga 文档有点不透明,我做错了什么?

13t*_*les 7

我找到了答案。这是关于生成器函数的本质。

generator.next()将生成器前进到下一yield条指令;为了进行这样的测试,实际上不需要模拟对 API 请求的响应,您只需将所需的值传递给生成器的next()方法即可将响应注入到生成器中。

因此,在我上面的测试代码中,有两次对next(). 第一次调用next()将导致 saga 发出 API 请求。第二个将前进到下一个yield,要么调度带有catch块的操作put(),要么调度catch块中的错误。诀窍在于,应在第二次调用中提供所需的模拟 API 响应next()