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

如何正确Mock TypeScript命名空间中的未导出属性?

如何Mock TypeScript命名空间中的私有变量

我需要测试以下TypeScript代码,目标是MockTiempo命名空间内的horaActual和luzNatural私有变量:

import dayjs, { Dayjs, OpUnitType } from 'dayjs';

export namespace Tiempo {

    let horaActual = dayjs().set("hour", 6).set("minute", 0).set("second", 0);
    let luzNatural = 1;

    export function obtenerHora(): string {
        return horaActual.format("HH:mm:ss");
    }

    export function avanzar(valor: number, unidad: OpUnitType = "millisecond"): Dayjs {
        horaActual = horaActual.add(valor, unidad);
        recalcularLuzNatural();
        return horaActual;
    }
    
    function recalcularLuzNatural(): void {
        const minutos = horaActual.minute() + (horaActual.hour() * 60);
        if(minutos >= 300 && minutos < 480) {
            luzNatural = 0.00635199 * Math.pow(1.01059529, minutos);
        }else if(minutos >= 1080 && minutos < 1260) {
            luzNatural = 2.14833262E+37 * Math.pow(minutos, -12.30691935);
        }else if(minutos >= 1260 || minutos < 300) {
            luzNatural = 0.1;
        }else {
            luzNatural = 1;
        }
    }

    export function obtenerLuzNatural(): number {
        return luzNatural;
    }

}

我尝试了手动Mock,但调用obtenerHora()时始终返回真实的horaActual值,而非Mock值:

jest.mock("../../../src/utils/tiempo/Tiempo", () => {
    const original = jest.requireActual("../../../src/utils/tiempo/Tiempo");
    const mock = original.Tiempo;
    mock.horaActual = dayjs().set("hour", 0).set("minute", 0).set("second", 0);
    return {
        Tiempo: mock,
    };
});

测试用例示例:

describe('obtenerHora', () => {

    it('Sin cambiar la hora inicial | devuelve la hora inicial', () => {
        console.log(Tiempo);
        const expectedTime = '00:00:00';
        expect(Tiempo.obtenerHora()).toBe(expectedTime);
    });

});

问题原因

horaActual和luzNatural是命名空间内用let声明的私有变量,没有被导出,属于模块内部作用域。你直接修改mock.horaActual只是给导出的Tiempo对象添加了一个新属性,完全不会影响内部函数访问的那个私有变量,两者不在同一个作用域。

正确的Mock方案

方案1:重写导出函数,模拟内部状态(无需修改原代码)

直接Mock整个Tiempo命名空间的导出函数,手动维护模拟的状态,确保函数行为符合预期:

import dayjs from 'dayjs';

jest.mock('../../../src/utils/tiempo/Tiempo', () => {
    // 模拟内部状态
    let mockHoraActual = dayjs().set('hour', 0).set('minute', 0).set('second', 0);
    let mockLuzNatural = 0.1;

    // 复制原模块的recalcularLuzNatural逻辑,保证模拟的行为和原代码一致
    const recalcularLuzNatural = () => {
        const minutos = mockHoraActual.minute() + (mockHoraActual.hour() * 60);
        if(minutos >= 300 && minutos < 480) {
            mockLuzNatural = 0.00635199 * Math.pow(1.01059529, minutos);
        }else if(minutos >= 1080 && minutos < 1260) {
            mockLuzNatural = 2.14833262E+37 * Math.pow(minutos, -12.30691935);
        }else if(minutos >= 1260 || minutos < 300) {
            mockLuzNatural = 0.1;
        }else {
            mockLuzNatural = 1;
        }
    };

    return {
        Tiempo: {
            obtenerHora: () => mockHoraActual.format('HH:mm:ss'),
            avanzar: (valor, unidad = 'millisecond') => {
                mockHoraActual = mockHoraActual.add(valor, unidad);
                recalcularLuzNatural();
                return mockHoraActual;
            },
            obtenerLuzNatural: () => mockLuzNatural,
        },
    };
});

这样你的测试用例就能正常验证obtenerHora()返回Mock的时间了。

方案2:调整原代码结构,让状态可访问(更优雅的测试方式)

如果可以修改原代码,建议把私有变量放到一个内部对象中,方便测试时Mock:

// 修改后的Tiempo模块
import dayjs, { Dayjs, OpUnitType } from 'dayjs';

export namespace Tiempo {
    // 用对象存储状态,测试时可通过该对象修改内部值
    const state = {
        horaActual: dayjs().set("hour", 6).set("minute", 0).set("second", 0),
        luzNatural: 1
    };

    export function obtenerHora(): string {
        return state.horaActual.format("HH:mm:ss");
    }

    export function avanzar(valor: number, unidad: OpUnitType = "millisecond"): Dayjs {
        state.horaActual = state.horaActual.add(valor, unidad);
        recalcularLuzNatural();
        return state.horaActual;
    }
    
    function recalcularLuzNatural(): void {
        const minutos = state.horaActual.minute() + (state.horaActual.hour() * 60);
        if(minutos >= 300 && minutos < 480) {
            state.luzNatural = 0.00635199 * Math.pow(1.01059529, minutos);
        }else if(minutos >= 1080 && minutos < 1260) {
            state.luzNatural = 2.14833262E+37 * Math.pow(minutos, -12.30691935);
        }else if(minutos >= 1260 || minutos < 300) {
            state.luzNatural = 0.1;
        }else {
            state.luzNatural = 1;
        }
    }

    export function obtenerLuzNatural(): number {
        return state.luzNatural;
    }

    // 仅用于测试,导出state(可选,或者通过jest.mock直接修改)
    export const __test__ = { state };
}

然后测试时可以直接修改state:

import dayjs from 'dayjs';
import { Tiempo } from '../../../src/utils/tiempo/Tiempo';

jest.mock('../../../src/utils/tiempo/Tiempo', () => {
    const original = jest.requireActual('../../../src/utils/tiempo/Tiempo');
    // 修改内部state的horaActual
    original.Tiempo.__test__.state.horaActual = dayjs().set('hour', 0).set('minute', 0).set('second', 0);
    return original;
});

这种方式不需要重复业务逻辑,测试更简洁,同时通过__test__命名约定明确这是测试专用的导出。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 03:54:57