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

Jest测试函数console.log输出异常:对立用例均通过问题排查

问题:Jest对立测试用例全部通过,逻辑验证失效

我有一个错误记录函数logData,非生产环境会输出堆栈追踪,生产环境仅输出基础错误信息,通过process.env.STAGE判断环境,函数逻辑本身没问题。但用Jest写的两个对立测试用例(一个带.not断言)居然全部通过,明显不符合预期,求排查错误。

现有测试代码

describe('Validate util.logData', ()=>{
    const log = console.log; // save original console.log function

    beforeEach(async () => {
        console.log = jest.fn(); // create a new mock function for each test
    });
    afterAll(() => {
        console.log = log; // restore original console.log after all tests
    });
    test('Validate util.logData will not output a stack trace in Prod. This should pass.', ()=>{
        process.env.STAGE='PROD'
        util.logData('JestTest', new Error('Jest Test'),['BETA', 'PROD']).then(()=>{
            expect(console.log).not.toHaveBeenCalledWith(expect.stringContaining('Error: Jest Test'));
        });
    });
    test('Validate util.logData will output a stack trace in Prod. This should fail.', ()=>{
        process.env.STAGE='PROD'
        util.logData('JestTest', new Error('Jest Test'),['BETA', 'PROD']).then(()=>{
            expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Error: Jest Test'));
        });
    });
})

函数代码

exports.logData = function(caller, toLog,logEnv=['LOCAL', 'GAMMA']){
    return new Promise((resolve,reject)=>{
        try{
            let message = "";
            const env = (process.env.STAGE) ? process.env.STAGE : 'LOCAL';
            logEnv = (Array.isArray(logEnv) && (typeof logEnv[0] === 'string' || logEnv[0] instanceof String))
                ?  logEnv.map(i=>i.toUpperCase()) : ['LOCAL', 'GAMMA'];
            if(logEnv.includes(env.toUpperCase())){
                if (typeof toLog === 'string' || toLog instanceof String) {
                    message = toLog;
                } else {
                    if(toLog.message) {
                        message += toLog.message;
                    }
                    if(env.toUpperCase() !== 'PROD') {
                        if (toLog.stack) {
                            message += "\n\t" + toLog.stack;
                        }
                        message += "\n\t" + JSON.stringify(toLog, Object.getOwnPropertyNames(toLog));
                    }
                }
                if(message !== ''){
                    const output = (new Date()).toUTCString() + " - " + caller + " - " + message
                    console.log(output);
                    resolve(output);
                }
            }
        } catch(error){
            reject(error);
        }
    });
}

错误原因分析

1. 异步测试未等待Promise完成

你的测试调用了返回Promise的util.logData,但没有告诉Jest要等待异步操作结束。Jest会在测试函数执行完(也就是调用完util.logData后)直接标记测试通过,完全不会等待then回调里的断言执行。这就导致不管断言是对是错,测试都直接通过,根本没验证到逻辑。

2. 测试环境未隔离

  • process.env.STAGE在测试间没有重置,前一个测试设置的PROD会影响后续测试,导致测试结果不可靠。
  • 用afterAll恢复console.log,如果某个测试失败,会导致后续测试的console.log一直是mock函数,污染全局状态。

3. 断言逻辑错误

第一个测试的断言完全错误:生产环境下函数依然会输出Error: Jest Test这个错误消息,只是不输出堆栈信息。你原断言是not.toHaveBeenCalledWith(expect.stringContaining('Error: Jest Test')),这本身就不符合函数逻辑。


修复方案

修复后的测试代码

describe('Validate util.logData', ()=>{
    const originalLog = console.log;
    const originalStage = process.env.STAGE;

    beforeEach(() => {
        // 每个测试前重置mock和环境变量
        console.log = jest.fn();
        process.env.STAGE = originalStage;
    });

    afterEach(() => {
        // 每个测试后恢复原始console.log
        console.log = originalLog;
    });

    // 使用async/await处理异步,确保Jest等待断言执行
    test('生产环境下仅输出基础错误信息,不包含堆栈', async ()=>{
        process.env.STAGE = 'PROD';
        await util.logData('JestTest', new Error('Jest Test'), ['BETA', 'PROD']);
        
        // 验证输出了基础错误信息
        expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Error: Jest Test'));
        // 验证没有输出堆栈信息
        expect(console.log).not.toHaveBeenCalledWith(expect.stringContaining('stack'));
    });

    test('非生产环境下输出错误信息及堆栈追踪', async ()=>{
        process.env.STAGE = 'LOCAL';
        await util.logData('JestTest', new Error('Jest Test'), ['LOCAL', 'GAMMA']);
        
        // 验证同时包含错误信息和堆栈
        expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Error: Jest Test'));
        expect(console.log).toHaveBeenCalledWith(expect.stringContaining('stack'));
    });
})

关键修复点

  1. 用async/await处理异步:测试函数标记为async,调用util.logData时加await,确保Jest等待Promise完成后再执行断言,真正验证逻辑。
  2. 隔离测试环境:在beforeEach中重置process.env.STAGE为原始值,避免测试间互相污染;用afterEach恢复console.log,确保每个测试都是独立的。
  3. 修正断言逻辑:根据函数实际行为调整断言——生产环境不是完全不输出错误信息,而是不输出堆栈,所以分别验证错误信息存在、堆栈不存在;非生产环境则验证两者都存在。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 14:39:52