【问题标题】:TypeError: Cannot read properties of undefined (reading 'then') - axios post nodejs jestTypeError: Cannot read properties of undefined (reading \'then\') - axios post nodejs jest
【发布时间】:2022-11-11 03:55:57
【问题描述】:

我正在尝试使用 jest 为 axios 发布请求编写单元测试。 这是我的实际功能-

exports.getAccessToken = function (urlToCall, scope, basicAuthToken) {
  return new Promise(function (resolve, reject) {
    let axios = require("axios");
    let qs = require("qs");
    let data = qs.stringify({
      grant_type: "client_credentials",
      scope: scope,
    });
    let config = {
      method: "post",
      url: urlToCall,
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        Authorization: "Basic " + basicAuthToken,
      },
      data: data,
    };

    axios(config)
      .then(function (response) {
        resolve(response.data);
      })
      .catch(function (error) {
        console.log(
          "error occurred while getting access token for the scope - ",
          scope,
          " and the error is - ",
          error
        );
      });
  });
};

这是我的单元测试代码 -

const processUtils = require('../src/utils/process-utils')
const axios = require('axios')
jest.mock("axios")
describe("when getAccessToken API is successful", () => {
    test('should return access token', async () => {
        const expectedResponse = JSON.stringify({
            "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImMxZDY2OTF",
            "issued_token_type": "token-type:access_token",
            "token_type": "Bearer",
            "expires_in": 3600,
            "scope": "consumer_profile:read:"
        })
        axios.post.mockResolvedValueOnce(() => Promise.resolve(expectedResponse))
        // axios.post.mockImplementationOnce(() => Promise.resolve(expectedResponse));

        let urlToCall = 'https://somehost.com/access_token/v1'
        let scope = jest.fn
        let basicAuthToken = jest.fn

        const response = await processUtils.getAccessToken(urlToCall, scope, basicAuthToken)

        expect(mockAxios.post).toHaveBeenCalledWith(urlToCall)
        expect(response).toEqual(expectedResponse)
    });
});

这是运行 jest 时抛出的错误 -

TypeError: Cannot read properties of undefined (reading 'then')

> axios(config)
    .then(function (response) {
       resolve(response.data);
    })

https://i.stack.imgur.com/NZiVp.png 我是节点和笑话的新手。有人可以指出我在这里缺少什么吗?

【问题讨论】:

  • 在导出函数中包含requires 是很不寻常的,我不确定它的行为方式。您是否尝试将它们放在exports 之外?

标签: node.js axios jestjs


【解决方案1】:

问题是由于您的代码没有直接在axios 模块实例上调用post 函数,而是通过config 隐式执行它,而您的测试模拟正在寻找直接的axios.post称呼。有 2 种方法可以解决此问题。

  1. 将隐式post 调用更改为显式调用:

    从:

    axios(config).then(function (response) {
    

    至:

    axios.post(config.url, config.data, { headers: config.headers }).then(function (response) {
    

    这将使用axios.post.mockResolvedValueOnce 调用的结果。

    1. 在测试套件设置中模拟 axios 后调用:

    从:

    jest.mock("axios")
    

    jest.mock("axios", () => {
      const expectedResponse = JSON.stringify({
        "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImMxZDY2OTF",
        "issued_token_type": "token-type:access_token",
        "token_type": "Bearer",
        "expires_in": 3600,
        "scope": "consumer_profile:read:"
      });
      return () => new Promise((resolve) => resolve(expectedResponse));
    })
    

    这将通过 a 模拟隐式 post 调用部分的模拟,但是您将无法直接访问 post 方法,因此您将无法收听它的调用。

    另一个小提示,axios.post.mockResolvedValueOnce(() => Promise.resolve(expectedResponse)) 将在调用then 时将一个函数传递给response 参数。我认为您需要使用mockedAxios.post.mockResolvedValueOnce(expectedResponse)。此外,expectedResponse 应包含在 data 属性中,如下所示:

    const expectedResponse = JSON.stringify({
      "data": {
        "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImMxZDY2OTF",
        "issued_token_type": "token-type:access_token",
        "token_type": "Bearer",
        "expires_in": 3600,
        "scope": "consumer_profile:read:"
      }
    });
    

【讨论】:

  • 感谢您的答复。我进行了两项更改 - 将隐式后调用更改为显式调用,并在测试套件设置中模拟 axios 后调用。不知何故,它没有模拟并给出“未定义”响应,如下所示的测试输出 - 预期:“{“access_token”:“eyJhbGciOiJSUzI1NiIsImtpZCI6ImMxZDY2OTF”,“issued_token_type”:“token-type:access_token”,“token_type”:“Bearer” ,"expires_in":3600,"scope":"consumer_profile:read:"}" 收到:未定义
  • 如果您正在进行显式调用(编号 1),则不需要进行测试套件设置(编号 2),这是一个替代建议。
  • 谢谢你的帮助。我的问题得到了解决。问题是我的实际方法已经返回了 Promise,但我的模拟方法也返回了 Promise 对象。而不是 return () => new Promise((resolve) => resolve(expectedResponse));我正在返回返回预期响应;
  • 好东西,如果它帮助您解决问题中的原始问题,您介意接受这个答案吗?谢谢!
【解决方案2】:

我正在尝试发布解决方案,以便对某人有所帮助。 也根据 Ovidijus Parsiunas 的响应找到了解决方案。 实际功能:

exports.getAccessToken = function (urlToCall, scope, basicAuthToken) {
  return new Promise(function (resolve, reject) {
    let axios = require("axios");
    let qs = require("qs");
    let data = qs.stringify({
      grant_type: "client_credentials",
      scope: scope,
    });
    const requestHeaders = {
      "Content-Type": "application/x-www-form-urlencoded",
      Authorization: "Basic " + basicAuthToken,
    };
    // axios(config)
    axios
      .post(urlToCall, data, {
        headers: requestHeaders,
      })
      .then(function (response) {
        resolve(response.data);
      })
      .catch(function (error) {
        console.log(
          "error occurred while getting access token for the scope - ",
          scope,
          " and the error is - ",
          error
        );
      });
  });
};

我的单元测试用例:

const processUtils = require('../src/utils/process-utils')
const axios = require('axios')
jest.mock("axios", () => ({
  post: jest.fn(() => {
    const expectedResponse = {
      access_token: "eyJhbGciOiJSUzI1NiIsImtpZCI6ImMxZDY2OTF",
      issued_token_type: "token-type:access_token",
      token_type: "Bearer",
      expires_in: 3600,
      scope: "consumer_profile:read:",
    };
    // return () => new Promise((resolve) => resolve(expectedResponse));
    return expectedResponse;
  }),
}));
describe("when getAccessToken API is successful", () => {
  it("should return access token", async () => {
    const expectedResponse = {
      data: {
        access_token: "eyJhbGciOiJSUzI1NiIsImtpZCI6ImMxZDY2OTF",
        issued_token_type: "token-type:access_token",
        token_type: "Bearer",
        expires_in: 3600,
        scope: "consumer_profile:read:",
      },
    };

    axios.post.mockResolvedValueOnce(expectedResponse);

    let urlToCall =
      "https://somehost.com/access_token/v1";
    let scope = jest.fn();
    let basicAuthToken = jest.fn();

    const response = await processUtils.getAccessToken(
      urlToCall,
      scope,
      basicAuthToken
    );
    console.log("response - ", response);
    expect(response).toEqual(expectedResponse.data);
  });

【讨论】:

    猜你喜欢
    • 2022-01-11
    • 2022-10-06
    • 2021-11-10
    • 2021-11-03
    • 2021-11-08
    • 2022-09-23
    • 2021-12-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多