如何使用 axios / jest 测试失败的请求

时间:2021-06-30 18:50:19

标签: javascript unit-testing axios jestjs

我创建了一个非常小的应用程序,如果您传入硬币和数量,它会计算为某些加密货币支付的总价。我想测试错误,但我总是收到“收到的承诺已解决而不是被拒绝”。我相信这是因为如果 url 错误,axios 仍然可以解决承诺。

我遇到的第二个问题是,我已经尝试测试 url 是否正确,但是我遇到了一个问题,即标头是 url 的一部分,我无法弄清楚如何仅测试没有标头的 url 路径。

那些是测试 2 和 3(测试编号 1 有效)

// getTotalPrice.js
const axios = require("axios");

let symbol = process.argv[2];
let quantity = process.argv[3];

const API = `https://rest.coinapi.io/v1/exchangerate`;
const headers = {headers:{"X-CoinAPI-Key": "MY TOKEN"},};

const getTotalPrice = async (symbol = "BTC", quantity = 1) => {
  try {
  let res = await axios.get(`${API}/${symbol}/USD`, headers);
  let data = res.data;
  return Math.round(data.rate * quantity * 100) / 100;
  } catch(err) {
    console.log(err)
  }
};

getTotalPrice(symbol, quantity);

module.exports = {
  getTotalPrice,
  API
};

// getTotalPrice.test.js

const { get, getError } = require("axios");
const { getTotalPrice } = require("../getTotalPrice");
describe("getTotalPrice", () => {
  afterEach(() => {
    get.mockClear();
  });
  it("fetches data successfully from an api", async () => {
    const res = {
      data: {
        rate: 34000,
      },
    };
    get.mockImplementation(() => Promise.resolve(res));
    await expect(getTotalPrice()).resolves.toEqual(res.data.rate);
  });
  it("throws an error when incorrect data is passed", async () => {
    const errorMessage = "Wrong inputs passed in";

    getError.mockImplementationOnce(() => Promise.reject({}));
    await expect(getTotalPrice()).rejects.toThrowError();
  });
  it("uses correct url", async () => {
    const data = {
      data: {
        rate: 2000,
      },
    };
    get.mockImplementationOnce(() => Promise.resolve(data));

    await getTotalPrice("ETH");
    expect(get).toHaveBeenCalledWith(
      `${API}/ETH/USD`
    );
  });
});

// axios.js (in __mocks__)

module.exports = {
  get: jest.fn(() => Promise.resolve({ data: {} })),
  getError: jest.fn(() => Promise.reject()),
};

总而言之,测试 1 通过,测试 2 失败,并显示“收到的承诺已解决而不是被拒绝”,测试 3 失败,因为我不仅获取了 url,还获取了标头。

1 个答案:

答案 0 :(得分:0)

您应该使用已解决/拒绝的值来模拟 axios.get()。由于您使用了 try...catch... 语句,因此 axios.get() 方法抛出的错误将被捕获,并且您没有重新抛出任何错误。所以测试用例 2 的断言将不匹配 toThrowError。相反,您可以使用 jest.spyOn() 将间谍添加到 console.log,并断言它会被模拟的 Error 调用。

这是一个仅使用 jest.mock('axios') 模拟 axios 模块而没有 __mocks__/axios.js 文件的解决方案。

例如

getTotalPrice.js

const axios = require('axios');

let symbol = process.argv[2];
let quantity = process.argv[3];

const API = `https://rest.coinapi.io/v1/exchangerate`;
const headers = { headers: { 'X-CoinAPI-Key': 'MY TOKEN' } };

const getTotalPrice = async (symbol = 'BTC', quantity = 1) => {
  try {
    let res = await axios.get(`${API}/${symbol}/USD`, headers);
    let data = res.data;
    return Math.round(data.rate * quantity * 100) / 100;
  } catch (err) {
    console.log(err);
  }
};

module.exports = { getTotalPrice, API };

getTotalPrice.test.js

const axios = require('axios');
const { getTotalPrice, API } = require('./getTotalPrice');

jest.mock('axios');

describe('getTotalPrice', () => {
  it('fetches data successfully from an api', async () => {
    const res = { data: { rate: 34000 } };
    axios.get.mockResolvedValueOnce(res);
    await expect(getTotalPrice()).resolves.toEqual(res.data.rate);
  });
  it('throws an error when incorrect data is passed', async () => {
    const logSpy = jest.spyOn(console, 'log');
    const err = new Error('Wrong inputs passed in');
    axios.get.mockRejectedValueOnce(err);
    await getTotalPrice();
    expect(logSpy).toBeCalledWith(err);
  });
  it('uses correct url', async () => {
    const res = { data: { rate: 2000 } };
    axios.get.mockResolvedValueOnce(res);
    await getTotalPrice('ETH');
    expect(axios.get).toHaveBeenCalledWith(`${API}/ETH/USD`, { headers: { 'X-CoinAPI-Key': 'MY TOKEN' } });
  });
});

测试结果:

 PASS  examples/68200193/getTotalPrice.test.js (7.527 s)
  getTotalPrice
    ✓ fetches data successfully from an api (3 ms)
    ✓ throws an error when incorrect data is passed (14 ms)
    ✓ uses correct url (1 ms)

  console.log
    Error: Wrong inputs passed in
        at /Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/examples/68200193/getTotalPrice.test.js:14:17
        at Generator.next (<anonymous>)
        at /Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/examples/68200193/getTotalPrice.test.js:8:71
        at new Promise (<anonymous>)
        at Object.<anonymous>.__awaiter (/Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/examples/68200193/getTotalPrice.test.js:4:12)
        at Object.<anonymous> (/Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/examples/68200193/getTotalPrice.test.js:12:66)
        at Object.asyncJestTest (/Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:106:37)
        at /Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/node_modules/jest-jasmine2/build/queueRunner.js:45:12
        at new Promise (<anonymous>)
        at mapper (/Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/node_modules/jest-jasmine2/build/queueRunner.js:28:19)
        at /Users/dulin/workspace/github.com/mrdulin/jest-v26-codelab/node_modules/jest-jasmine2/build/queueRunner.js:75:41

      at console.<anonymous> (node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866:25)
          at Generator.throw (<anonymous>)

------------------|---------|----------|---------|---------|-------------------
File              | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
------------------|---------|----------|---------|---------|-------------------
All files         |     100 |      100 |     100 |     100 |                   
 getTotalPrice.js |     100 |      100 |     100 |     100 |                   
------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        8.034 s