【问题标题】:How do I test axios in Jest?如何在 Jest 中测试 axios?
【发布时间】:2017-12-14 10:09:36
【问题描述】:

我在 React 中有这个动作:

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

在这种情况下如何测试Axios

Jest 在他们的网站上有一个异步代码用例,他们使用模拟函数,但我可以用 Axios 做到这一点吗?

参考:An Async Example

到目前为止,我已经这样做了,以测试它是否返回了正确的类型:

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

如何传入模拟数据并测试它是否返回?

【问题讨论】:

标签: javascript reactjs react-redux axios jestjs


【解决方案1】:

不使用任何其他库:

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: {...} }));

可以根据参数改变mock:

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

进一步阅读:

【讨论】:

    【解决方案2】:

    我使用了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

    【讨论】:

    • 如果我不想模拟 axios 并希望它发出真正的请求怎么办?
    【解决方案3】:

    我可以按照以下步骤进行操作:

    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;
        })
    };
    

    【讨论】:

    • 嗨@shorif2000 我在这里实现了它github.com/vspedr/movile-messaging/pull/8/files
    • mocks 中编写手动模拟并不是一个好习惯,因为 the manual mocks documentation 说在 __mocks__ 中编写手动模拟
    • @Januartha 对不起,我打错了。我要在这里纠正它。我输入了回复“____mock___”,它把我的话加粗了。对不起
    • @AmadeuCavalcanteFilho 啊当然,没问题:)
    【解决方案4】:

    我已经用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...
        })
    })
    

    【讨论】:

    • 我确实尝试过这个,但我似乎 axios 不能很好地与 nock 配合使用。github.com/node-nock/nock/issues/699 但仍然感谢您的帮助
    • nock 是在测试中处理 http 调用的最佳方式
    【解决方案5】:

    看看这个

    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);
      });
    

    【讨论】:

      【解决方案6】:

      对于那些希望使用 axios-mock-adapter 代替 Redux 文档 for async testing 中的 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;
      

      【讨论】:

      • 你是如何使用localStorageMock的
      • @Kermit_ice_tea src/setupTests.js 记录在 create-react-app 中,作为 jest/enzyme 测试的全局设置。在那个文件中,我创建了一个对象,我用虚拟函数(getItem、setItem)任意调用了localStorageMock。神奇之处在于我将global.localStorage 设置为等于该对象的底部。我可以在一行中完成此操作,而不是命名对象 localStorageMock。整个设置的目的是简单地避免任何处理本地存储的代码在测试期间中断。
      猜你喜欢
      • 2020-11-24
      • 2021-06-09
      • 2021-05-04
      • 1970-01-01
      • 2021-06-04
      • 2019-04-28
      • 2021-01-14
      • 2019-09-29
      • 2021-08-25
      相关资源
      最近更新 更多