close
  • 简体中文
  • Mock functions

    Rstest 基于 tinyspy 提供了一些工具方法帮助你进行函数的模拟(mock)。

    rs.fn

    • 别名: rstest.fn
    • 类型:
    export interface Mock<T extends Function> extends MockInstance<T> {
      (...args: Parameters<T>): ReturnType<T>;
    }
    
    export type MockFn = <T extends Function>(fn?: T) => Mock<T>;

    创建一个 mock 函数。

    const sayHi = rs.fn((name: string) => `hi ${name}`);
    
    const res = sayHi('bob');
    
    expect(res).toBe('hi bob');
    
    expect(sayHi).toHaveBeenCalledTimes(1);

    rs.spyOn

    • 别名: rstest.spyOn
    • 类型:
    export type SpyFn = (
      obj: Record<string, any>,
      methodName: string,
      accessType?: 'get' | 'set',
    ) => MockInstance;

    对一个对象的方法进行 mock。

    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();

    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

    • 别名: rstest.mocked
    • 类型:
    type MockedFn = <T>(
      item: T,
      deep?: boolean | { partial?: boolean; deep?: boolean },
    ) => Mocked<T>;

    一个 TypeScript 类型辅助函数,用于将对象包装为 mock 类型而不改变其运行时行为。当你 mock 了一个模块并想要获得正确的 mock 方法类型提示时,这很有用。

    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.callsmock.instancesmock.contextsmock.results 属性。

    rs.resetAllMocks

    • 别名: rstest.resetAllMocks
    • 类型: () => RstestUtilities

    清除所有 mock 属性,并将每个 mock 的实现重置为其原始实现。

    rs.restoreAllMocks

    • 别名: rstest.restoreAllMocks
    • 类型: () => RstestUtilities

    重置所有 mock,并恢复被 mock 的对象的原始描述符。

    更多