Javascript 如何测试redux saga';调用外部api的生成器?

Javascript 如何测试redux saga';调用外部api的生成器?,javascript,reactjs,redux,jestjs,redux-saga,Javascript,Reactjs,Redux,Jestjs,Redux Saga,在编写测试方面,我是一个完全的初学者,但我有任务测试一些api的实现,这是使用redux和redux saga完成的。该场景是用户用他的用户名、密码填写准备好的表单,我的生成器函数“authenticateUser”从api调用适当的端点(api处理程序由Swagger自动生成,并从私有注册表导入),以创建令牌并检查数据库中是否存在用户。如果是这样,令牌将保存到会话/本地存储,并使用相应的值更新存储 import { userAuthenticated, userAuthenticationR

在编写测试方面,我是一个完全的初学者,但我有任务测试一些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”进行简单的单元测试,但在第一次生成之后,它甚至无法进入“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

});
当我看集成测试时,这似乎是首选的方式,我不能简单地将相对简单的示例转移到我的特定案例中


我非常感谢您的帮助,因为我不知道从哪里开始。

您需要为响应对象传入一个模拟值。我认为它是未定义的,如果
response.status
不是200,您的代码就会抛出。因此,对于您的se,只需调用
generator.next().value
就可以像这样在模拟中传递:

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

你就不能嘲笑你的@my_模块吗?剩下的就行了。我曾经使用这个lib,这个lib的结果更好,但这取决于你,我遵循了你的宝贵建议,这使我更深入地研究了javascipt的生成器,在写我的问题时,我似乎并不理解。我设法将适当的属性传递给next()和mock“response”,但无法对mock“body”执行相同的操作,我尝试将其解析为JSON(const body=yield call([response,“JSON”])。每次它抛出错误并拒绝授权。我不确定语法是否正确。我认为你不需要通过数组。相反,我认为首先是函数,然后是数据。由于json是一个响应方法(并且您没有任何需要传递的参数),所以在将其传递给调用时可能必须绑定它。因此,
调用(response.json.bind(response))
另外,请确保您的mock有一个将返回承诺的
.json
方法。您的语法是正确的。我想这并不明显是错的,但不适合我的情况。我设法让它使用某种变通方法:
constparseresponse=res=>res.json();const body=yield调用(parseResponse,response)