簡體   English   中英

如何在 Jest 中測試 axios?

[英]How do I test axios in Jest?

我在 React 中有這個動作:

export function fetchPosts() {
    const request = axios.get(`${WORDPRESS_URL}`);
    return {
        type: FETCH_POSTS,
        payload: request
    }
}

在這種情況下如何測試Axios

Jest 在他們的網站上有這個用例,用於異步代碼,他們使用模擬函數,但我可以用 Axios 做到這一點嗎?

參考:一個異步示例

到目前為止,我已經這樣做了以測試它是否返回了正確的類型:

it('should dispatch actions with the correct type', () => {
    store.dispatch(fetchPosts());
    let action = store.getActions();
    expect(action[0].type).toBe(FETCH_POSTS);
});

如何傳入模擬數據並測試它是否返回?

不使用任何其他庫:

import * as axios from "axios";

// Mock out all top level functions, such as get, put, delete and post:
jest.mock("axios");

// ...

test("good response", () => {
  axios.get.mockImplementation(() => Promise.resolve({ data: {...} }));
  // ...
});

test("bad response", () => {
  axios.get.mockImplementation(() => Promise.reject({ ... }));
  // ...
});

可以指定響應代碼:

axios.get.mockImplementation(() => Promise.resolve({ status: 200, data: {...} }));

可以根據參數更改模擬:

axios.get.mockImplementation((url) => {
    if (url === 'www.example.com') {
        return Promise.resolve({ data: {...} });
    } else {
        //...
    }
});

Jest v23 引入了一些用於模擬 Promises 的語法糖:

axios.get.mockImplementation(() => Promise.resolve({ data: {...} }));

它可以簡化為

axios.get.mockResolvedValue({ data: {...} });

被拒絕的承諾也有一個等價物: mockRejectedValue

延伸閱讀:

我使用了 axios-mock-adapter 在這種情況下,服務在 ./chatbot 中進行了描述。 在模擬適配器中,您指定使用 API 端點時要返回的內容。

import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import chatbot from './chatbot';

describe('Chatbot', () => {
    it('returns data when sendMessage is called', done => {
        var mock = new MockAdapter(axios);
        const data = { response: true };
        mock.onGet('https://us-central1-hutoma-backend.cloudfunctions.net/chat').reply(200, data);

        chatbot.sendMessage(0, 'any').then(response => {
            expect(response).toEqual(data);
            done();
        });
    });
});

你可以在這里看到整個例子:

服務: https ://github.com/lnolazco/hutoma-test/blob/master/src/services/chatbot.js

測試: https ://github.com/lnolazco/hutoma-test/blob/master/src/services/chatbot.test.js

我可以按照以下步驟進行操作:

  1. 創建一個文件夾__mocks__/ (正如@Januartha 評論所指出的)
  2. 實現一個axios.js模擬文件
  3. 測試中使用我實現的模塊

模擬將自動發生

模擬模塊示例:

module.exports = {
    get: jest.fn((url) => {
        if (url === '/something') {
            return Promise.resolve({
                data: 'data'
            });
        }
    }),
    post: jest.fn((url) => {
        if (url === '/something') {
            return Promise.resolve({
                data: 'data'
            });
        }
        if (url === '/something2') {
            return Promise.resolve({
                data: 'data2'
            });
        }
    }),
    create: jest.fn(function () {
        return this;
    })
};

我已經用nock完成了這個,就像這樣:

import nock from 'nock'
import axios from 'axios'
import httpAdapter from 'axios/lib/adapters/http'

axios.defaults.adapter = httpAdapter

describe('foo', () => {
    it('bar', () => {
        nock('https://example.com:443')
            .get('/example')
            .reply(200, 'some payload')

        // test...
    })
})

看這個

  1. 測試album.js的函數
const fetchAlbum = function () {
 return axios
   .get("https://jsonplaceholder.typicode.com/albums/2")
   .then((response) => {
     return response.data;
   });
};
  1. 測試album.test.js
const axios = require("axios");
const { fetchAlbum } = require("../utils.js");

jest.mock("axios");

test("mock axios get function", async () => {
    expect.assertions(1);
    const album = {
      userId: 1,
      id: 2,
      title: "sunt qui excepturi placeat culpa",
    };
    const payload = { data: album };
    // Now mock axios get method
    axios.get = jest.fn().mockResolvedValue(payload);
    await expect(fetchAlbum()).resolves.toEqual(album);
  });

對於那些希望使用 axios-mock-adapter 代替Redux文檔中的 mockfetch 示例進行異步測試的人,我成功地使用了以下內容:

文件actions.test.js

describe('SignInUser', () => {
  var history = {
    push: function(str) {
        expect(str).toEqual('/feed');
    }
  }

  it('Dispatches authorization', () => {
    let mock = new MockAdapter(axios);
    mock.onPost(`${ROOT_URL}/auth/signin`, {
        email: 'test@test.com',
        password: 'test'
    }).reply(200, {token: 'testToken' });

    const expectedActions = [ { type: types.AUTH_USER } ];
    const store = mockStore({ auth: [] });

    return store.dispatch(actions.signInUser({
        email: 'test@test.com',
        password: 'test',
      }, history)).then(() => {
        expect(store.getActions()).toEqual(expectedActions);
  });

});

為了在文件actions/index.js中測試signInUser的成功案例:

export const signInUser = ({ email, password }, history) => async dispatch => {
  const res = await axios.post(`${ROOT_URL}/auth/signin`, { email, password })
    .catch(({ response: { data } }) => {
        ...
  });

  if (res) {
    dispatch({ type: AUTH_USER });                 // Test verified this
    localStorage.setItem('token', res.data.token); // Test mocked this
    history.push('/feed');                         // Test mocked this
  }
}

鑒於這是通過開玩笑來完成的,因此必須模擬 localstorage 調用。 這是在文件src/setupTests.js 中

const localStorageMock = {
  removeItem: jest.fn(),
  getItem: jest.fn(),
  setItem: jest.fn(),
  clear: jest.fn()
};
global.localStorage = localStorageMock;

自從最初回答了這個問題以來,已經引入了新的測試工具。

模擬的問題在於您經常測試模擬而不是代碼的真實上下文,從而使該上下文的某些區域未經測試。 對告訴 axios 返回什么承諾的改進是通過 Service Worker 攔截 http 請求。

Service Worker是您的 Web 應用程序和外部世界之間的客戶端可編程代理。 因此,與其模擬承諾解決方案,不如模擬代理服務器本身,攔截要測試的請求,這是一個更廣泛的解決方案。 由於攔截發生在網絡級別,您的應用程序對模擬一無所知。

您可以使用msw ( Mock Service Worker ) 庫來做到這一點。 這是一個簡短的視頻,解釋了它是如何工作的。

我能想到的最基本的設置是這樣的: 1️⃣設置handlers,類似於express.js的路由方法; 2️⃣設置模擬服務器並將處理程序作為參數傳遞; 3️⃣配置測試,以便模擬服務器攔截我們的請求; 4️⃣進行測試; 5️⃣關閉模擬服務器。

假設您要測試以下功能:

import axios from "axios";

export const fetchPosts = async () => {
  const request = await axios.get("/some/endpoint/");
  return {
    payload: request,
  };
};

然后測試可能如下所示:

import { rest } from "msw";
import { setupServer } from "msw/node";
import fetchPosts from "./somewhere";

// handlers are usually saved in separate file(s) in one  destined place of the app,
// so that you don't have to search for them when the endpoints have changed
const handlers = [ 1️⃣
  rest.get("/some/endpoint/", (req, res, ctx) =>
    res(ctx.json({ message: "success" }))
  ),
];

const server = setupServer(...handlers); 2️⃣

beforeAll(() => {
  server.listen(); 3️⃣
});

describe("fetchPosts", () => {
  it("should return 'success' message", async () => {
    const resp = await fetchPosts();
    expect(resp.payload?.data?.message).toEqual("success"); 4️⃣
  });
});

afterAll(() => {
  server.close(); 5️⃣
});

根據您使用的框架,配置可能會有所不同。 可以在 MSW 的repo上找到 React(REST 和 GraphQL)和 Angular 等一些通用示例。 VueMastery提供了一個 Vue 示例。 您還可以在 MSW 的食譜頁面上找到示例。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM