【问题标题】:How to test redux-saga's generator that calls external api?如何测试调用外部 api 的 redux-saga 的生成器?
【发布时间】:2019-09-15 23:07:15
【问题描述】:

在编写测试方面,我完全是初学者,但我被分配了测试一些 api 实现的任务,这是使用 redux 和 redux-saga 完成的。场景是用户用他的用户名、密码填写准备好的表单,我的生成器函数“authenticateUser”从 api 调用正确的端点(api 处理程序是从 Swagger 自动生成的,并从私有注册表导入),以创建令牌并检查用户是否存在在数据库中。如果是这样,则将令牌保存到会话/本地存储中,并使用相应的值更新存储。


import { userAuthenticated, userAuthenticationRejected } from "../actions/user";
import { call, put } from "redux-saga/effects";
import createApi from "@my-module";

export function* authenticateUser(action) {
  const myApi = yield call(createApi, {
    cors: true,
    securityHandlers: {
      Basic: () => true,
      Bearer: () => true
    }
  });

  try {
    const response = yield call(myApi.token_create, {
      data: {
        username: action.payload.username,
        password: action.payload.password
      }
    });
    if (response.status === 200) {
      const body = yield call([response, "json"]);
      const accessToken = body.access;
      window.sessionStorage.setItem("accessToken", accessToken);
      if (action.payload.persistAuthenticationToken) {
        window.localStorage.setItem("accessToken", accessToken);
      }

      yield put(
        userAuthenticated({
          username: action.payload.username,
          accessToken
        })
      );
    } else {
      throw new Error("Unauthorized");
    }
  } catch (error) {
    yield put(userAuthenticationRejected());
  }
}

在阅读有关测试方法的信息时,我完全迷失了方向。我已经尝试使用连续的“generator.next().value”进行简单的单元测试,但是在第一次 yield 之后它甚至无法进入“try”块并且比预期的更快地抛出“undefined”。

test("User authentication", () => {
  const action = {
    payload: {
      username: "test",
      password: "test"
    }
  };
  const generator = cloneableGenerator(authenticateUser)(action);
  console.log(generator.next().value);

  // {
  //   '@@redux-saga/IO': true,
  //   combinator: false,
  //   type: 'CALL',
  //   payload: {
  //     context: null,
  //     fn: [Function: createApi],
  //     args: [ [Object] ]
  //   }
  // }

  console.log(generator.next().value);

  // {
  //   '@@redux-saga/IO': true,
  //   combinator: false,
  //   type: 'PUT',
  //   payload: {
  //     channel: undefined,
  //     action: { type: 'USER_AUTHENTICATION_REJECTED' }
  //   }
  // }

  console.log(generator.next().value);

  // undefined

});

当我查看似乎是首选方式的集成测试时,我不能简单地将相对简单的示例转移到我的具体案例中。

我会很感激任何帮助,因为我不知道从哪里开始。

【问题讨论】:

标签: javascript reactjs redux jestjs redux-saga


【解决方案1】:

您需要为响应对象传入一个模拟值。我认为它是未定义的,如果response.status 不是 200,你的代码就会抛出异常。所以对于你自己来说,对generator.next().value 的调用会像这样传递一个模拟:

const responseMock = {
    status: 200,
     // anything else it needs
}
console.log(generator.next(reponseMock).value)

【讨论】:

  • 我听从了您的宝贵建议,这使我深入研究了 javascipt 的生成器,在撰写我的问题时我似乎并不理解。我设法将适当的属性传递给 next() 并模拟“响应”,但不能对模拟“body”做同样的事情,我尝试将其解析为 JSON (const body = yield call([response, "json" ]))。每次它抛出错误并拒绝授权。
  • 我不确定语法是否正确。我认为您不需要传入数组。相反,我认为首先是功能,然后是数据。由于 json 是一种响应方法(并且您没有任何需要传递的参数),因此您可能必须在将其传递给 call 时绑定它。所以call(response.json.bind(response))
  • 另外,确保你的 mock 有一个 .json 方法会返回一个 promise。
  • 你对那个语法是正确的。我猜这并没有明显错误,但不适合我的情况。我设法让它使用一种解决方法:const parseResponse = res => res.json(); const body = yield call(parseResponse, response);
猜你喜欢
  • 2021-11-21
  • 1970-01-01
  • 2018-05-02
  • 2018-01-06
  • 2018-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-09
相关资源
最近更新 更多