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

如何实现Node.js的server.js 100%代码覆盖率及解决Jest测试报错

问题解决方案

一、修复Jest测试报错并完成服务器启动代码的单元测试

你遇到的"Property 'server' does not exist in the provided object"错误,是因为app对象本身没有server属性——server是你在if块里声明的局部变量,没法直接通过app访问。下面是修正后的测试代码,同时覆盖服务器启动的逻辑:

const express = require('express');

describe('Server', () => {
  beforeEach(() => {
    // 重置所有mock,清除模块缓存
    jest.clearAllMocks();
    jest.resetModules();
  });

  it('should start server and log address when NODE_ENV is not "test"', () => {
    // 1. 模拟环境变量为生产环境
    process.env.NODE_ENV = 'production';
    // 2. 构造模拟的server对象
    const mockServer = {
      address: jest.fn().mockReturnValue({ address: '127.0.0.1', port: 3000 }),
      listening: true
    };
    // 3. Mock express的listen方法
    jest.spyOn(express(), 'listen').mockReturnValue(mockServer);
    // 4. Mock console.log
    jest.spyOn(console, 'log').mockImplementation();

    // 5. 重新加载server模块(读取最新环境变量)
    require('./server');

    // 断言listen方法被正确调用
    expect(express().listen).toHaveBeenCalledWith(3000, expect.any(Function));
    // 断言address方法被调用
    expect(mockServer.address).toHaveBeenCalled();
    // 断言日志输出正确
    expect(console.log).toHaveBeenCalledWith(
      'Example app listening at : http://127.0.0.1:3000'
    );
  });

  it('should NOT start server when NODE_ENV is "test"', () => {
    process.env.NODE_ENV = 'test';
    const mockListen = jest.spyOn(express(), 'listen').mockImplementation();

    require('./server');

    // 断言listen从未被调用
    expect(mockListen).not.toHaveBeenCalled();
  });
});

关键修正点:

  • 不要尝试spyOn不存在的app.server,而是直接mockexpress().listen方法,返回自定义的模拟server对象
  • 测试前修改process.env.NODE_ENV并重置模块缓存,确保模块重新加载时读取最新环境变量
  • 模拟console.log来验证日志输出逻辑

二、实现server.js的100%代码覆盖率

要达到100%覆盖率,需要覆盖所有代码分支和语句,除了服务器启动逻辑,还要覆盖中间件注册、路由处理等部分。下面补充完整的测试用例:

1. 测试中间件与路由注册

const express = require('express');
const cors = require('cors');
const path = require('path');
const routes = require('./routes/route');

describe('Server middleware and routes', () => {
  beforeEach(() => {
    jest.resetModules();
    jest.mock('cors', () => jest.fn());
    jest.mock('./routes/route', () => jest.fn());
  });

  it('should register all required middleware', () => {
    const mockUse = jest.spyOn(express(), 'use').mockImplementation();
    require('./server');

    // 验证cors中间件被调用
    expect(cors).toHaveBeenCalled();
    // 验证静态文件中间件路径正确
    expect(mockUse).toHaveBeenCalledWith(express.static(path.resolve(__dirname, './build')));
    // 验证json解析中间件被注册
    expect(mockUse).toHaveBeenCalledWith(express.json());
    // 验证路由被注册
    expect(mockUse).toHaveBeenCalledWith('/', routes);
  });

  it('should redirect all unmatched routes to index.html', async () => {
    const supertest = require('supertest');
    const app = require('./server');
    const mockSendFile = jest.spyOn(require('express').response, 'sendFile').mockImplementation();

    await supertest(app).get('/random-path');

    // 验证sendFile被调用,路径正确
    expect(mockSendFile).toHaveBeenCalledWith(path.resolve(__dirname, './build', 'index.html'));
  });
});

2. 覆盖率配置

在jest.config.js中开启覆盖率收集并设置阈值:

module.exports = {
  collectCoverage: true,
  collectCoverageFrom: ['server.js'],
  coverageThreshold: {
    global: {
      lines: 100,
      branches: 100,
      functions: 100,
      statements: 100
    }
  }
};

覆盖所有分支的核心要点:

  • 覆盖process.env.NODE_ENV === "test"和!== "test"两个分支
  • 覆盖通配符GET *路由的重定向逻辑
  • 验证所有中间件的注册语句
  • 验证服务器启动回调中的server.address()调用和日志输出

内容的提问来源于stack exchange,提问作者ankit lohiya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 17:04:54