如何实现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
相关产品推荐
相关产品推荐

