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

