You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jest测试覆盖率未覆盖自定义Error子类构造函数中super(message)调用的问题求助

Fixing Jest Coverage for super(message) in Custom Error Classes

I've run into this exact issue before with Jest not tracking coverage for super(message) in custom Error subclasses—even when the error is being instantiated indirectly via static methods. Here's how to resolve it:

The Root Cause

Jest's coverage tool (whether using V8 or Babel) sometimes has trouble flagging the super(message) call as covered when it's only triggered indirectly through static factory methods. This isn't a bug so much as a quirk in how coverage is tracked for built-in class inheritance.

Solution 1: Write Explicit Tests for Your Error Class

The most reliable fix is to create a dedicated test file for your ApiError (and MongooseError) class, where you directly invoke the constructor. This ensures the super(message) line is explicitly hit and tracked.

Here's an example test file (api-error.test.ts):

import { ApiError } from './ApiError';

describe('ApiError', () => {
  // Test the constructor directly to cover super(message)
  it('initializes with correct properties via constructor', () => {
    const testStatusCode = 500;
    const testMessage = 'Internal Server Error';
    
    const error = new ApiError(testStatusCode, testMessage);
    
    expect(error).toBeInstanceOf(Error);
    expect(error).toBeInstanceOf(ApiError);
    expect(error.statusCode).toBe(testStatusCode);
    expect(error.message).toBe(testMessage);
    expect(error.name).toBe('ApiError');
  });

  // Verify your static factory methods work as expected (bonus coverage)
  describe('static factory methods', () => {
    it('creates a 400 Bad Request error', () => {
      const error = ApiError.badRequest('Invalid input');
      expect(error.statusCode).toBe(400);
      expect(error.message).toBe('Invalid input');
    });

    it('creates a 401 Unauthorized error', () => {
      const error = ApiError.unauthorized('Login required');
      expect(error.statusCode).toBe(401);
      expect(error.message).toBe('Login required');
    });

    it('creates a 403 Forbidden error', () => {
      const error = ApiError.forbidden('No access');
      expect(error.statusCode).toBe(403);
      expect(error.message).toBe('No access');
    });
  });
});

Repeat this pattern for your MongooseError class with a corresponding mongoose-error.test.ts file.

Solution 2: Add a Quick Constructor Test to Existing Suites

If you don't want to create separate test files, you can add a tiny test case to one of your existing controller/service test suites that directly instantiates the error:

// In one of your existing test files
it('covers ApiError constructor super call', () => {
  const error = new ApiError(500, 'Test coverage trigger');
  expect(error.statusCode).toBe(500);
});

Bonus: Clean Up Redundant Code

While not related to coverage, notice that your constructor has redundant lines that can be trimmed:

// Original constructor
constructor(public statusCode: number, message: string) {
  super(message);
  this.name = 'ApiError';
  this.statusCode = statusCode; // Redundant—already set via constructor parameter
  this.message = message; // Redundant—super(message) already sets this
  Object.setPrototypeOf(this, ApiError.prototype);
}

Simplified version:

constructor(public statusCode: number, message: string) {
  super(message);
  this.name = 'ApiError';
  // Object.setPrototypeOf is only needed if targeting older JavaScript environments
  Object.setPrototypeOf(this, ApiError.prototype);
}

Modern TypeScript handles the prototype chain correctly for class inheritance, so you might even be able to remove the Object.setPrototypeOf line depending on your target environment.

After implementing either solution, re-run your Jest coverage and the super(message) line should now be marked as covered.

内容的提问来源于stack exchange,提问作者João Casarin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 17:59:05