如何使用Jest在React Native项目中使用__mocks__

时间:2018-10-05 20:48:32

标签: typescript unit-testing react-native mocking jestjs

我正在构建一个React Native应用,并且正在使用Jest编写单元测试。

(从this question中可以看到,)我正在编写一个检查是否存在网络连接的函数。

以下是函数(checkNetwork.ts):

import { NetInfo } from "react-native";
import { NO_NETWORK_CONNECTION } from "../../../../config/constants/errors";

const checkNetwork = (): Promise<boolean | string> =>
  new Promise((resolve, reject) => {
    NetInfo.isConnected
      .fetch()
      .then(isConnected => (isConnected ? resolve(true) : reject(NO_NETWORK_CONNECTION)))
      .catch(() => reject(NO_NETWORK_CONNECTION));
  });

export default checkNetwork;

现在,我想在测试另一个进行API调用的功能时模拟此功能。

我在__mocks__内与checkNetwork.ts相邻的位置创建了一个名为checkNetwork/的文件夹(请参见下面的文件夹结构)。在其中,我还创建了另一个名为checkNetwork.ts的文件,其中包含模拟内容,如下所示:

const checkNetwork = () => new Promise(resolve => resolve(true));

export default checkNetwork;

使用此函数的函数会发出一个简单的提取请求(postRequests.ts):

import checkNetwork from "../../core/checkNetwork/checkNetwork";

export const postRequestWithoutHeader = (fullUrlRoute: string, body: object) =>
  checkNetwork().then(() =>
    fetch(fullUrlRoute, {
      method: "POST",
      body: JSON.stringify(body),
      headers: { "Content-Type": "application/json" }
    }).then(response =>
      response.json().then(json => {
        if (!response.ok) {
          return Promise.reject(json);
        }
        return json;
      })
    )
  );

文件夹结构如下:

myreactnativeproject
  ├── app/
  │   ├── services/
  │   │   ├── utils/
  │   │   │    └── core/
  │   │   │        └── checkNetwork/
  │   │   │              └── checkNetwork.ts
  │   │   ├── serverRequests/
  │   │   │    └── POST/
  │   │   │        └── postRequests.ts
  │   .   .
  │   .   .
  │   .   .
  .
  .
  .

然后我在postRequests.test.ts内创建了另一个名为POST/的文件,以编写postRequests.test.ts的单元测试。我现在希望Jest自动使用返回true的模拟。但是实际上发生的是该测试未能返回NO_NETWORK_CONNECTION。如何获得Jest使用该模拟游戏?

1 个答案:

答案 0 :(得分:0)

这是解决方案,文件夹结构无关紧要,为简单起见,像这样的文件夹结构:

.
├── __mocks__
│   └── checkNetwork.ts
├── checkNetwork.ts
├── errors.ts
├── postRequests.test.ts
└── postRequests.ts

checkNetwork.ts

import NetInfo from '@react-native-community/netinfo';
import { NO_NETWORK_CONNECTION } from './errors';

const checkNetwork = (): Promise<boolean | string> =>
  new Promise((resolve, reject) => {
    NetInfo.isConnected
      .fetch()
      .then(isConnected => (isConnected ? resolve(true) : reject(NO_NETWORK_CONNECTION)))
      .catch(() => reject(NO_NETWORK_CONNECTION));
  });

export default checkNetwork;

postRequests.ts

import checkNetwork from './checkNetwork';
import fetch from 'node-fetch';

export const postRequestWithoutHeader = (fullUrlRoute: string, body: object) =>
  checkNetwork().then(() =>
    fetch(fullUrlRoute, {
      method: 'POST',
      body: JSON.stringify(body),
      headers: { 'Content-Type': 'application/json' }
    }).then(response =>
      response.json().then(json => {
        if (!response.ok) {
          return Promise.reject(json);
        }
        return json;
      })
    )
  );

单元测试:

postRequests.test.ts

import { postRequestWithoutHeader } from './postRequests';
import fetch from 'node-fetch';

const { Response } = jest.requireActual('node-fetch');

jest.mock('./checkNetwork.ts');
jest.mock('node-fetch');

describe('postRequestWithoutHeader', () => {
  const mockedData = { data: 'mocked data' };
  const mockedJSONData = JSON.stringify(mockedData);
  const urlRoute = 'https://github.com/mrdulin';
  const body = {};

  it('should post request without header correctly', async () => {
    (fetch as jest.MockedFunction<typeof fetch>).mockResolvedValueOnce(new Response(mockedJSONData));
    const actualValue = await postRequestWithoutHeader(urlRoute, body);
    expect(actualValue).toEqual(mockedData);
    expect(fetch).toBeCalledWith(urlRoute, {
      method: 'POST',
      body: JSON.stringify(body),
      headers: { 'Content-Type': 'application/json' }
    });
  });

  it('should post request error', async () => {
    const mockedResponse = new Response(mockedJSONData, { status: 400 });
    (fetch as jest.MockedFunction<typeof fetch>).mockResolvedValueOnce(mockedResponse);
    await expect(postRequestWithoutHeader(urlRoute, body)).rejects.toEqual(mockedData);
    expect(fetch).toBeCalledWith(urlRoute, {
      method: 'POST',
      body: JSON.stringify(body),
      headers: { 'Content-Type': 'application/json' }
    });
  });
});

覆盖率100%的单元测试结果:

 PASS  src/stackoverflow/52673113/postRequests.test.ts
  postRequestWithoutHeader
    ✓ should post request without header correctly (7ms)
    ✓ should post request error (1ms)

-----------------|----------|----------|----------|----------|-------------------|
File             |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-----------------|----------|----------|----------|----------|-------------------|
All files        |      100 |      100 |      100 |      100 |                   |
 postRequests.ts |      100 |      100 |      100 |      100 |                   |
-----------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        4.996s, estimated 5s

以下是完整的演示:https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/52673113

依赖项:

"@react-native-community/netinfo": "^4.2.1",
"react-native": "^0.60.5",

我正在使用最新版本的react-native模块,因此不推荐使用NetInfo模块。 https://facebook.github.io/react-native/docs/netinfo.html