开玩笑的分支覆盖率为零

IT技术 reactjs jestjs enzyme
2021-05-19 10:39:44

我写了一些测试用例,除了下面的一个,一切似乎都很好。我得到一个文件的零分支覆盖。我在谷歌上搜索了几个博客,我开始了解语句 cab 是否在调用分支覆盖的多个场景中执行。但我发现我的代码不能以多种方式执行。

请求.js

import axios from 'axios';

export default async (request, httpService = axios) => {
  const {
    method, url, data, headers,
  } = request;
  return httpService.request({
    method,
    url,
    headers: Object.assign({}, headers),
    data,
  });
};

请求.test.js

describe('requestServie', () => {
  it('should have a valid request object', async () => {
    const requestObj = {
      method: 'POST',
      url: 'http://mock.url',
      data: {},
    };

    const mockRequest = jest.fn(() => Promise.resolve({}));

    const httpService = {
      request: mockRequest,
    };

    await request(requestObj, httpService);
    expect(mockRequest).toHaveBeenCalledWith({
      method: requestObj.method,
      url: requestObj.url,
      headers: {},
      data: requestObj.data,
    });
  });

  it('should return a valid response (empty)', async () => {
    const response = {
      data: {
      },
      status: 200,
      statusText: 'OK',
      headers: {},
      config: {},
      request: {},
    };

    const mockRequest = jest.fn(() => Promise.resolve(response));

    const httpService = {
      request: mockRequest,
    };

    const res = await request({ url: 'http://mock.url' }, httpService);

    expect(res).not.toBe(null);
    expect(res).toMatchObject(
      {
        status: response.status,
      },
    );
  });
});

编辑

请求.js

export default async (request, httpService = axios) => {
  const {
    method, url, data, headers,
  } = request;
  return httpService.request({
    method,
    url,
    headers: Object.assign({}, headers),
    data,
  }).then(successResponse, (error) => {
    throwHttpError(error);
  });
};

请求.test.js

import HttpError from 'standard-http-error';
import axios from 'axios';
import request, { successResponse, throwHttpError } from './requestService';

describe('requestService', () => {

  jest.mock('axios', () => ({
    request: jest.fn(() => Promise.resolve({})),
  }));

  describe('successResponse', () => {
    const mockRes = {
      status: 9001,
      data: {
        stuff: 'stuff',
      },
    };
    it('should returns an object with only status and data properties', () => {
      const responseKeys = Object.keys(successResponse(mockRes));
      expect(responseKeys).toMatchObject(['status', 'data']);
      expect(responseKeys.length).toBe(2);
    });

    it('should map the status of the reponse to the status property', () => {
      expect(successResponse(mockRes).status)
        .toBe(mockRes.status);
    });

    it('should map the data of the reponse to the data property', () => {
      expect(successResponse(mockRes).data)
        .toMatchObject(mockRes.data);
    });

    it('should have a valid request object', async () => {
      const requestObj = {
        method: 'POST',
        url: 'http://mock.url',
        data: {},
        headers: {},
      };

      const mockRequest = jest.fn(() => Promise.resolve({}));

      const httpService = {
        request: mockRequest,
      };

      await request(requestObj, httpService);
      expect(mockRequest).toHaveBeenCalledWith({
        method: requestObj.method,
        url: requestObj.url,
        headers: {},
        data: requestObj.data,
      });
    });
  });

  describe('httpThrowError', () => {
    const mockErr = {
      response: {
        status: 9001,
        statusText: 'error message goes here',
      },
    };

    it('should map the status of the reponse to the error.status property', () => {
      try {
        throwHttpError(mockErr);
      } catch (e) {
        expect(e).not.toBe(null);
        expect(e.status).toBe(mockErr.response.status);
        expect(e.message).toBe(mockErr.response.statusText);
      }
    });

    it('should map the data of the reponse to the error.data property', () => {
      const mockErrWithData = Object.assign({}, mockErr);
      mockErrWithData.response.data = {};
      try {
        throwHttpError(mockErrWithData);
      } catch (e) {
        expect(e).not.toBe(null);
        expect(e.data).toBe(mockErrWithData.response.data);
      }
    });
  });

  describe('request', () => {
    const testCases = [
      ['should return error response on server error', 500],
      ['should return error response on bad request', 400],
      ['should return error response on unauthorised', 401],
    ];

    testCases.forEach(([testName, errorStatus]) => {
      it(testName, async () => {
        const errorResponse = {
          response: {
            status: errorStatus,
          },
        };
        const mockRequest = jest.fn(() => Promise.reject(errorResponse));

        const httpService = {
          request: mockRequest,
        };

        try {
          await request({ url: 'http://mock.url' }, httpService);
          throw new Error('Expected an exception, but none was thrown');
        } catch (err) {
          expect(err).not.toBe(null);
          expect(err).toMatchObject(
            new HttpError(errorResponse.response.status,
              errorResponse.response.statusText),
          );
        }
      });
    });

    it('should return an valid response (empty)', async () => {
      const response = {
        data: {
          meta: {},
          results: [],
        },
        status: 200,
        statusText: 'OK',
        headers: {},
        config: {},
        request: {},
      };

      const mockRequest = jest.fn(() => Promise.resolve(response));

      const httpService = {
        request: mockRequest,
      };

      const res = await request({ url: 'http://mock.url' }, httpService);

      expect(res).not.toBe(null);
      expect(res).toMatchObject(
        {
          status: response.status,
          data: response.data,
        },
      );
    });

    it('should use axios by default', async () => {
      const req = { url: 'http://mock.url', method: 'get' };
      await request(req);
      expect(axios.request).toHaveBeenCalled();
    });
  });
});

错误

在此处输入图片说明

在此处输入图片说明

在此处输入图片说明

18 年 11 月 15 日更新

"jest": "^23.6.0",

import HttpError from 'standard-http-error';
import axios from 'axios';
import request, { successResponse, throwHttpError } from './requestService';

jest.mock('axios', () => ({
  request: jest.fn(),
}));

错误

在此处输入图片说明

1个回答

要查看未涵盖的内容,您可以前往coverage/Iconv-report并打开index.htmljest使用--coverage option运行后就会创建这些

看起来未覆盖的分支是:httpService = axios所以你需要检查是否使用了默认值axios

为了解决这个问题,您可以不带httpService参数地运行请求- 您可以为此进行全局模拟 axios,即:

import axios from 'axios';

// note that mock goes before any describe block

jest.mock('axios', () => {
  return {
    request: jest.fn(() => Promise.resolve({})),
  }
});

describe('requestService', () => {

  // ... your other tests

  it('should use axios by default', async () => {
    const opts = { url: 'http://mock.url', method: 'get' };
    const res = await request(opts);
    expect(axios.request).toHaveBeenCalled();
  });
});

请注意,在 spec 中运行时jest.mock会有一些错误的行为