Mock functions
Rstest 基于 tinyspy 提供了一些工具方法帮助你进行函数的模拟(mock)。
rs.fn
type FunctionLike = (...args: any) => any;
export interface Mock<
T extends FunctionLike = FunctionLike,
> extends MockInstance<T> {
new (...args: Parameters<T>): ReturnType<T>;
(...args: Parameters<T>): ReturnType<T>;
}
export type MockFn = <T extends FunctionLike = FunctionLike>(fn?: T) => Mock<T>;
创建一个 mock 函数。
如果需要为可调用的 mock 函数标注类型,请参考 Mock 类型。
const sayHi = rs.fn((name: string) => `hi ${name}`);
const res = sayHi('bob');
expect(res).toBe('hi bob');
expect(sayHi).toHaveBeenCalledTimes(1);
export type SpyFn = <T extends Record<string, any>, K extends keyof T>(
obj: T,
methodName: K,
accessType?: 'get' | 'set',
) => MockInstance<T[K]>;
对一个对象的方法进行 mock。
关于 spy 返回的控制 API,请参考 MockInstance 类型。
const sayHi = () => 'hi';
const hi = {
sayHi,
};
const spy = rs.spyOn(hi, 'sayHi');
expect(hi.sayHi()).toBe('hi');
expect(spy).toHaveBeenCalled();
对同一个方法重复调用 rs.spyOn 时,会返回已有的 spy,而不是重新定义。
const hi = {
sayHi: () => 'hi',
};
rs.spyOn(hi, 'sayHi').mockImplementation(() => 'hello');
expect(hi.sayHi()).toBe('hello');
// 返回的 spy 实例与第一次调用相同
expect(rs.spyOn(hi, 'sayHi')).toBeCalled();
对 re-export 或第三方模块的导出进行 spy
rs.spyOn 能作用于你所导入模块中直接定义的导出,但对从其他模块 re-export(export * from '...')或由第三方依赖提供的导出可能不生效。
这种情况下,请改用 { spy: true } 来 mock 该模块。它会在保留真实实现的同时,为每个导出装上 spy:
rs.mock('pkg', { spy: true });
rs.isMockFunction
- 别名:
rstest.isMockFunction
- 类型:
(fn: any) => fn is MockInstance
判断给定的函数是否为 mock 函数。
rs.mockObject
- 别名:
rstest.mockObject
- 类型:
type MockObject = <T>(
object: T,
options?: { spy?: boolean },
) => MaybeMockedDeep<T>;
创建一个对象的深度 mock。所有方法都会被替换为 mock 函数,而原始值和普通对象会保留。
基本用法
const original = {
method() {
return 42;
},
nested: {
getValue() {
return 'real';
},
},
prop: 'foo',
};
const mocked = rs.mockObject(original);
// 方法默认返回 undefined
expect(mocked.method()).toBe(undefined);
expect(mocked.nested.getValue()).toBe(undefined);
// 原始值保持不变
expect(mocked.prop).toBe('foo');
// 方法是 mock 函数
expect(rs.isMockFunction(mocked.method)).toBe(true);
Mock 返回值
你可以配置 mock 方法返回特定的值:
const mocked = rs.mockObject({
fetchData: () => 'real data',
});
mocked.fetchData.mockReturnValue('mocked data');
expect(mocked.fetchData()).toBe('mocked data');
Spy 模式
当传入 { spy: true } 作为第二个参数时,原始实现会被保留,同时仍然追踪调用:
const original = {
add: (a: number, b: number) => a + b,
};
const spied = rs.mockObject(original, { spy: true });
// 保留原始实现
expect(spied.add(1, 2)).toBe(3);
// 追踪调用
expect(spied.add).toHaveBeenCalledWith(1, 2);
expect(spied.add.mock.results[0]).toEqual({ type: 'return', value: 3 });
数组
默认情况下,数组会被替换为空数组。使用 { spy: true } 时,数组保持其原始值:
const mocked = rs.mockObject({ array: [1, 2, 3] });
expect(mocked.array).toEqual([]);
const spied = rs.mockObject({ array: [1, 2, 3] }, { spy: true });
expect(spied.array).toEqual([1, 2, 3]);
Mock 类
你也可以 mock 类构造函数。使用 { spy: true } 可以保留原始类的行为,同时追踪调用:
class UserService {
getUser() {
return { id: 1, name: 'Alice' };
}
}
// 使用 { spy: true } 保留原始实现
const MockedService = rs.mockObject(UserService, { spy: true });
const instance = new MockedService();
// 原始方法正常工作
expect(instance.getUser()).toEqual({ id: 1, name: 'Alice' });
// 覆盖实现
rs.mocked(instance.getUser).mockImplementation(() => ({ id: 2, name: 'Bob' }));
expect(instance.getUser()).toEqual({ id: 2, name: 'Bob' });
rs.mocked
type MockedFn = <T>(
item: T,
deepOrOptions?: boolean | { partial?: boolean; deep?: boolean },
) =>
| Mocked<T>
| MaybeMockedDeep<T>
| MaybePartiallyMocked<T>
| MaybePartiallyMockedDeep<T>;
一个 TypeScript 类型辅助函数,用于将对象包装为 mock 类型而不改变其运行时行为。当你 mock 了一个模块并想要获得正确的 mock 方法类型提示时,这很有用。
推导出的返回类型会随选项变化:{ deep: true } 会递归应用 mock 类型,{ partial: true } 则使用 partial mock 类型。
如果需要为 mock 对象或模块标注类型,请参考 Mocked 类型。
import { myModule } from './myModule';
rs.mock('./myModule', { spy: true });
// TypeScript 现在知道 myModule.method 是一个 MockInstance
const mockedModule = rs.mocked(myModule);
mockedModule.method.mockReturnValue('mocked');
该函数在运行时只是返回相同的对象——它只影响 TypeScript 类型。
rs.clearAllMocks
- 别名:
rstest.clearAllMocks
- 类型:
() => RstestUtilities
清除所有 mock 的 mock.calls、mock.instances、mock.contexts 和 mock.results 属性。
rs.resetAllMocks
- 别名:
rstest.resetAllMocks
- 类型:
() => RstestUtilities
清除所有 mock 属性,并将每个 mock 的实现重置为其原始实现。
rs.restoreAllMocks
- 别名:
rstest.restoreAllMocks
- 类型:
() => RstestUtilities
重置所有 mock,并恢复被 mock 的对象的原始描述符。
更多