【问题标题】:Issues mocking return value of Axios post call for unit test问题模拟 Axios 后调用的返回值以进行单元测试
【发布时间】:2019-12-09 14:01:34
【问题描述】:

试图模拟 Axios 调用 单元测试来自我们身份软件的令牌响应。 Axios 根本没有被调用,因此我的返回总是未定义的。

我已经尝试将 Axios 调用更改为 axios.post 并更改我已经多次模拟的方式,然后我可以数数。我不认为我应该为 Axios 安装另一个模拟框架来模拟这个功能。

实施:

async getAuthToken() {        
    const oauthUrl = process.env.OAUTHURL;
    const oauthAudience = process.env.OAUTHAudience;
    const oauthUsername = process.env.OAUTHUSERNAME;
    const oauthPassword = process.env.OAUTHPASSWORD;
    
    let urlForAuth = oauthUrl
    urlForAuth = urlForAuth + '/as/token.oauth2?';
    urlForAuth = urlForAuth + 'grant_type=client_credentials';
    urlForAuth = urlForAuth + '&aud=' + oauthAudience + '/';
    urlForAuth = urlForAuth + '&scope=' + oauthAudience + '/.read';
    
    const options = {
        method: 'POST',
        url: urlForAuth,
        headers: {
            'Authorization': "Basic " + Buffer.from(oauthUsername + ":" + oauthPassword).toString("base64")
        },
        responseType: 'json'
    };
    
    try{
        let response = await axios(options); 
        return response.data.access_token;
    }
    catch(e){
        console.log(e);
        throw e;
    }       
}

测试用例:

test('token Is Returned', async () => {
    expect.hasAssertions();

    let Response = `{
            "access_token": "thisisatoken",
            "token_type": "Bearer",
            "expires_in": 3599
        }`;

    axios = jest.fn(() => Promise.resolve());

    axios.mockImplementationOnce(() =>
        Promise.resolve({
            data: Response
        })
    );            
    
    let response = await AuthService.getAuthToken();

    expect(axios).toHaveBeenCalledTimes(1);
    expect(response).toEqual("thisisatoken");  
});

我得到的错误是

预期的模拟函数被调用了一次,但它被调用了零次。

当我调试响应中的数据元素时,包含以下内容:

data:"Copyright (c) 2019 Next Small Things\n"

这在我的代码中没有。帮助。

【问题讨论】:

  • 您需要知道为什么会发生这种情况Expected mock function to have been called one time, but it was called zero times. 这是因为您希望jest.fn() 已被调用了1 次expect( jest.fn() ).toHaveBeenCalledTimes( 1 ) 但该函数从未调用过。所以请阅读这里的文档jestjs.io/docs/en/expect#tohavebeencalledtimesnumber
  • @Ekaputra 我相信作者明白这一点。并询问我们没有调用 mock 的原因可能是什么。

标签: node.js unit-testing axios jestjs mocking


【解决方案1】:

你不能用这种方式模拟事物。实际上,您只是在为您的测试代码模拟 axios,而不是为被测组件单独导入 axios

你需要正确地模拟模块并且你实际上有plenty of options:

  1. __mocks__ 文件夹中提供即用型模拟
  2. 调用jest.mock('axios') 获取自动生成的模拟(每个exported 成员将自动变为jest.fn
  3. 提供工厂模拟jest.mock('axios', () => { .... return object like it all are exported from file ... })

您还需要将axios 导入到您的测试中才能访问它:

import axios from 'axios';

jest.mock('axios');

test('token Is Returned', async () => {
    expect.hasAssertions();

    let Response = `{
            "access_token": "thisisatoken",
            "token_type": "Bearer",
            "expires_in": 3599
        }`;

    axios.mockReturnValue(() =>
        Promise.resolve({
            data: Response
        })
    );            

    let response = await AuthService.getAuthToken();

    expect(axios).toHaveBeenCalledTimes(1);
    expect(response).toEqual("thisisatoken");  
});

注意几件事:

  1. jest.mock is hoisted 到顶部,即使它是在测试代码中非常深的地方声明的。因此,最好将所有 jest.mock 放在文件的顶部 - 因为它无论如何都是这样工作的 - 这样其他开发人员就不会感到困惑,无论它是否被模拟。
  2. 如果使用带有自动模拟功能的 __mocks__ 文件夹,您最好提前注入 jest.fn() - 大多数情况下,您希望检查是否已调用模拟的一部分以及使用哪些参数
  3. jest.mock 不能引用任何同级变量,但名称以 mock... 开头的变量除外。请参阅 Service mocked with Jest causes "The module factory of jest.mock() is not allowed to reference any out-of-scope variables" error 并提供非常好的解释。
  4. 很难(几乎不可能)部分地取消模拟模块。所以考虑你的测试要么模拟某个模块,要么根本不模拟来测试它。

【讨论】:

  • 虽然这里的代码块没有解决我的问题,但您发布的信息使我彻底改变了我的实现并获得了我正在寻找的东西。谢谢。
  • 令人惊讶 :) 也许您可以编写自己的答案并将其标记为合适的答案?这可以帮助别人
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-03-28
  • 2020-04-06
  • 2016-03-21
  • 2015-03-01
  • 2017-03-13
  • 2011-11-26
  • 1970-01-01
相关资源
最近更新 更多