close
  • English
  • Mock functions

    Rstest provides some utility functions to help you mock functions powered by tinyspy.

    rs.fn

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

    Creates a spy on a function.

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

    rs.spyOn

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

    Creates a spy on a method of an object.

    const sayHi = () => 'hi';
    const hi = {
      sayHi,
    };
    
    const spy = rs.spyOn(hi, 'sayHi');
    
    expect(hi.sayHi()).toBe('hi');
    
    expect(spy).toHaveBeenCalled();

    If you call rs.spyOn multiple times for the same method, Rstest reuses the existing spy instead of redefining it.

    const hi = {
      sayHi: () => 'hi',
    };
    rs.spyOn(hi, 'sayHi').mockImplementation(() => 'hello');
    
    expect(hi.sayHi()).toBe('hello');
    // should get the same spy instance
    expect(rs.spyOn(hi, 'sayHi')).toBeCalled();

    rs.isMockFunction

    • Alias: rstest.isMockFunction
    • Type: (fn: any) => fn is MockInstance

    Determines if the given function is a mocked function.

    rs.mockObject

    • Alias: rstest.mockObject
    • Type:
    type MockObject = <T>(
      object: T,
      options?: { spy?: boolean },
    ) => MaybeMockedDeep<T>;

    Creates a deep mock of an object. All methods are replaced with mock functions, while primitive values and plain objects are preserved.

    Basic usage

    const original = {
      method() {
        return 42;
      },
      nested: {
        getValue() {
          return 'real';
        },
      },
      prop: 'foo',
    };
    
    const mocked = rs.mockObject(original);
    
    // Methods return undefined by default
    expect(mocked.method()).toBe(undefined);
    expect(mocked.nested.getValue()).toBe(undefined);
    
    // Primitive values are preserved
    expect(mocked.prop).toBe('foo');
    
    // Methods are mock functions
    expect(rs.isMockFunction(mocked.method)).toBe(true);

    Mocking return values

    You can configure mocked methods to return specific values:

    const mocked = rs.mockObject({
      fetchData: () => 'real data',
    });
    
    mocked.fetchData.mockReturnValue('mocked data');
    
    expect(mocked.fetchData()).toBe('mocked data');

    Spy mode

    When { spy: true } is passed as the second argument, the original implementations are preserved while still tracking calls:

    const original = {
      add: (a: number, b: number) => a + b,
    };
    
    const spied = rs.mockObject(original, { spy: true });
    
    // Original implementation is preserved
    expect(spied.add(1, 2)).toBe(3);
    
    // Calls are tracked
    expect(spied.add).toHaveBeenCalledWith(1, 2);
    expect(spied.add.mock.results[0]).toEqual({ type: 'return', value: 3 });

    Arrays

    By default, arrays are replaced with empty arrays. With { spy: true }, arrays keep their original values:

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

    Mocking classes

    You can also mock class constructors. Use { spy: true } to preserve the original class behavior while tracking calls:

    class UserService {
      getUser() {
        return { id: 1, name: 'Alice' };
      }
    }
    
    // Use { spy: true } to keep original implementation
    const MockedService = rs.mockObject(UserService, { spy: true });
    const instance = new MockedService();
    
    // Original method works
    expect(instance.getUser()).toEqual({ id: 1, name: 'Alice' });
    
    // Override the implementation
    rs.mocked(instance.getUser).mockImplementation(() => ({ id: 2, name: 'Bob' }));
    expect(instance.getUser()).toEqual({ id: 2, name: 'Bob' });

    rs.mocked

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

    A type helper for TypeScript that wraps an object with mock types without changing its runtime behavior. This is useful when you have mocked a module and want proper type hints for the mock methods.

    import { myModule } from './myModule';
    
    rs.mock('./myModule', { spy: true });
    
    // TypeScript now knows myModule.method is a MockInstance
    const mockedModule = rs.mocked(myModule);
    
    mockedModule.method.mockReturnValue('mocked');

    The function simply returns the same object at runtime - it only affects TypeScript types.

    rs.clearAllMocks

    • Alias: rstest.clearAllMocks
    • Type: () => RstestUtilities

    Clears the mock.calls, mock.instances, mock.contexts and mock.results properties of all mocks.

    rs.resetAllMocks

    • Alias: rstest.resetAllMocks
    • Type: () => RstestUtilities

    Clears all mocks properties and reset each mock's implementation to its original.

    rs.restoreAllMocks

    • Alias: rstest.restoreAllMocks
    • Type: () => RstestUtilities

    Reset all mocks and restore original descriptors of spied-on objects.

    More