【问题标题】:Why does this mock api not work as expected?为什么这个模拟 api 不能按预期工作?
【发布时间】:2017-02-23 22:19:19
【问题描述】:

我正在尝试测试这个简单的 api 模块:

import fetch from 'isomorphic-fetch';

export const getJson = (endpoint: string) => {
  const options = { credentials: 'include', method: 'GET' };

  return fetch(endpoint, options)
    .then(response => response.json()
      .then(json => {
        if (response.ok) return json;
        return Promise.reject(json.errors);
      })
    )
    .catch(error => {
      if (error.constructor === Array) return error;
      return [error.message];
    });
};

通过这个测试,我正在模拟 fetch:

import { getJson } from '../api';

const mockResponse = (status, statusText, response) => {
  return new window.Response(response, {
    status: status,
    statusText: statusText,
    headers: {
      'Content-type': 'application/json'
    }
  });
};

describe('api middleware', () => {
  describe('getJson', () => {
    it('should return the response on success', () => {
      const expected = { data: ['data'], meta: {} };
      const body = JSON.stringify(expected);

      window.fetch = jest.fn().mockImplementation(() =>
        Promise.resolve(mockResponse(200, null, body)));

      return getJson('http://endpoint').then(actual => expect(actual).toEqual(expected));
    });
  });
});

但测试失败:

Expected value to equal:
  {"data": ["data"], "meta": {}}
Received:
  ["Unexpected end of JSON input"]

Difference:

Comparing two different types of values:
  Expected: object
  Received: array

我无法弄清楚为什么这不起作用。为什么我会收到“JSON 输入意外结束”错误?以及如何在测试中成功模拟本地获取?在this medium post 中它的完成方式基本相同..

【问题讨论】:

  • 我在项目的根目录下模拟了isomorphic-fetch。如果我删除测试结果是:["request to http://endpoint failed, reason: getaddrinfo ENOTFOUND endpoint endpoint:80"]。所以看起来开玩笑是自动使用那个模拟。但是,我的模拟实现仍然没有取代 fetch。
  • 可能导致您必须使用global 而不是window

标签: javascript reactjs ecmascript-6 jestjs fetch-api


【解决方案1】:

所以显然测试仍在使用全局 fetch 库,而不是我的修补版本。解决方案是:

  1. 删除“isomorphic-fetch”模拟(在项目根目录的__mocks__ 中)。
  2. 使用import 'isomorphic-fetch; 在我的项目的根目录处导入一次“isomorphic-fetch”
  3. 删除我的 api 模块顶部的“isomorphic-fetch”导入(因为它已在入口点导入
  4. 将测试更新为:

测试:

// to make the Response constructor available
import 'isomorphic-fetch';
import { getJson } from '../api';

describe('api middleware', () => {
  describe('getJson', () => {
    beforeEach(() => {
      window.fetch = jest.genMockFunction();
    });

    it('should return the response on success', () => {
      const expected = { data: ['data'], meta: {} };
      const body = JSON.stringify(expected);
      const init = { status: 200, statusText: 'OK' };

      window.fetch.mockReturnValueOnce(Promise.resolve(new Response(body, init)));

      return getJson('http://endpoint').then(actual => expect(actual).toEqual(expected));
    });
  });
});

【讨论】:

  • 为什么这被否决了?它解决了我上面发布的问题。
【解决方案2】:

很可能是因为您的getJson 函数不使用全局(窗口)fetch

我建议这样做的方式是使用Dependency Injection (DI);使getJson 检索“http 请求”库/函数(在您的情况下为fetch)并在您的测试中创建一个注入的模拟函数。作为测试的一部分,模拟函数将返回您想要的数据。

【讨论】:

  • 你的意思是因为它是从 isomorphic-fetch 包中导入的?
  • 如果这是问题所在,那么我将如何从测试中模拟从 isomorphic-fetch 中的提取?
  • 为什么投反对票?从我所描述的另一个答案中可以看出,这正是它不起作用的原因。有关解决方案的更多信息,请参阅我的更新答案。
  • 我想是因为你的答案一开始只有一行。即使它可能是正确的(我不确定)。我的回答也被否决了,所以不知道..