jest-mock-extended

repository·master·Indexed 21 days ago

https://github.com/marchaos/jest-mock-extended

Type-safe mocking extensions for Jest (version 4.0.1) that provide full TypeScript support for mocking interfaces, objects, and classes. It includes utilities like mock() for shallow mocks, mockDeep() for nested hierarchies, and stub() for lightweight proxies. The library extends standard Jest mocks with the calledWith() method for argument-specific behavior and provides a comprehensive set of asymmetric matchers, captors, and state management functions like mockClear() and mockReset().

Tokens
3.4K
Snippets
16
Records
16
Agent score
75%

What's inside jest-mock-extended

  1. Create deep mocks with mockDeep()

    master

    The mockDeep function creates a DeepMockProxy<T>, where nested objects and functions are also automatically mocked. This is useful for complex, deeply nested dependency trees.

    There are two ways to call mockDeep:

    1. mockDeep<T>(mockImplementation?: DeepPartial<T>): Pass a partial implementation of the object.
    2. mockDeep<T>(opts: { funcPropSupport?: true; fallbackMockImplementation?: MockOpts['fallbackMockImplementation'] }, mockImplementation?: DeepPartial<T>): Pass configuration options first.

    Note on funcPropSupport: When funcPropSupport is set to true, properties that are functions in the original type are treated as both a CalledWithMock and a DeepMockProxy, allowing for more flexible nested mocking.

    import { mockDeep } from 'jest-mock-extended';
    
    interface DeepService {
      client: {
        connection: {
          query: () => Promise<string>;
        };
      };
    }
    
    const service = mockDeep<DeepService>();
    
    // Deeply nested properties are automatically mocked
    service.client.connection.query.mockResolvedValue('success');
  2. Use asymmetric matchers for argument expectations

    master

    When using jest-mock-extended, you can use asymmetric matchers to validate that a mock was called with specific types or patterns rather than exact values. These matchers are passed into Jest's expectation functions (like toHaveBeenCalledWith).

    Available matchers include:

    • Type-based matchers: any(), anyBoolean(), anyNumber(), anyString(), anyFunction(), anySymbol(), anyObject(), anyArray(), anyMap(), anySet().
    • Instance checking: isA(Class) checks if a value is an instance of a specific class.
    • Collection matchers: arrayIncludes(value), setHas(value), mapHas(value), objectContainsKey(key), objectContainsValue(value).
    • Nullability matchers: notNull(), notUndefined(), notEmpty().
    • Custom logic: matches(matcherFn) allows you to provide a custom predicate function.
    import { anyString, isA, arrayIncludes } from 'jest-mock-extended';
    
    // Example usage in a Jest test
    expect(mock.method).toHaveBeenCalledWith(anyString());
    expect(mock.method).toHaveBeenCalledWith(isA(MyClass));
    expect(mock.method).toHaveBeenCalledWith(arrayIncludes('expected-item'));
  3. Create a deep mock with mockDeep()

    master

    Use mockDeep() to create a mock object where all nested properties and methods are also automatically mocked. This returns a DeepMockProxy, which is useful for complex objects with deep hierarchies where you want to avoid manually mocking every sub-property.

    import { mockDeep } from 'jest-mock-extended';
    
    const deepMock = mockDeep<ComplexType>();
    // deepMock.nestedProperty.method() is also a mock
  4. Use calledWithFn() for argument-specific mock behavior

    master

    The calledWithFn utility allows you to define different mock implementations based on the specific arguments passed to a function. Instead of a single global mock implementation, you can chain .calledWith(...) calls to return different values or execute different logic for specific argument sets.

    When a call matches a previously defined set of arguments (using either literal values or matchers), the specific mock associated with those arguments is executed. If no match is found, the fallbackMockImplementation (if provided) is used.

    import { calledWithFn } from 'jest-mock-extended';
    
    // Example usage pattern:
    const myMock = calledWithFn((arg: string) => `Hello ${arg}`);
    
    // You can define specific behaviors for specific arguments
    myMock.calledWith('world')(); 
    
    // When called with 'world', it uses the specific mock returned by .calledWith()
    // When called with anything else, it falls back to the original implementation
  5. Create a stub with stub()

    master

    The stub<T>() function creates a proxy of type T. Unlike mock(), it does not create complex Jest mocks for every property. Instead, if you access a property that does not exist on the object, it returns a jest.fn(). This is useful for lightweight stubs where you only care about a few specific method calls.

    import { stub } from 'jest-mock-extended';
    
    interface Simple {
      doSomething: () => void;
    }
    
    const s = stub<Simple>();
    // s.doSomething is a jest.fn()
    s.doSomething();
  6. Create a shallow mock with mock()

    master

    The mock function creates a MockProxy<T>. This proxy automatically mocks all properties of the type T. If a property is a function, it is automatically turned into a Jest mock function with the calledWith extension.

    • mock(mockImplementation?: DeepPartial<T>, opts?: MockOpts)
    • opts.deep: (Boolean) If true, enables deep mocking (see mockDeep).
    • opts.fallbackMockImplementation: (Function) A function to provide a default implementation for mocked properties.
    import { mock } from 'jest-mock-extended';
    
    interface User {
      id: string;
      getName: () => string;
    }
    
    const userMock = mock<User>();
    
    // userMock.getName is now a jest.fn()
    userMock.getName.mockReturnValue('John Doe');
  7. Create custom matchers with matches()

    master

    If the built-in matchers do not satisfy your requirements, you can create a custom matcher using the matches() function. This function accepts a predicate function (a MatcherFn<T>) that returns true if the value matches your criteria.

    matches<T>((actualValue: T) => boolean)

    import { matches } from 'jest-mock-extended';
    
    // Create a matcher that checks if a string starts with a specific prefix
    const startsWithHello = matches((val: string) => val.startsWith('Hello'));
    
    expect(mock.method).toHaveBeenCalledWith(startsWithHello());
  8. Clear and Reset mocks with mockClear() and mockReset()

    master

    These utilities allow you to recursively clear or reset all mock state within a MockProxy or DeepMockProxy.

    • mockClear(mock: MockProxy<any>): Clears the state of all mock functions (calls, instances, etc.) within the proxy and its nested mocks.
    • mockReset(mock: MockProxy<any>): Resets all mock functions to their initial state (removes any mocked implementations) within the proxy and its nested mocks.
    import { mock, mockClear, mockReset } from 'jest-mock-extended';
    
    const service = mock<MyService>();
    
    // ... perform tests ...
    
    mockClear(service); // Clears call history
    mockReset(service); // Clears history AND implementations
  9. Capture arguments using captor()

    master

    The captor() utility allows you to both validate an argument in an expectation and extract the actual value(s) passed to a mock for further assertions.

    A CaptorMatcher acts as an asymmetric matcher that, when matched, stores the actualValue in its .value property and pushes all matched values into its .values array.

    Use this when you need to perform complex assertions on the arguments that are difficult to express with standard matchers.

    import { captor } from 'jest-mock-extended';
    
    const myCaptor = captor<MyType>();
    
    // Use the captor in the expectation
    expect(mock.method).toHaveBeenCalledWith(myCaptor());
    
    // Access the captured values for detailed assertions
    const capturedValue = myCaptor.value;
    const allCapturedValues = myCaptor.values;
    
    expect(capturedValue.someProperty).toBe('specific-value');
  10. Configure global mock behavior with JestMockExtended

    master

    Use JestMockExtended.configure to set global configuration for mocks. A common use case is using ignoreProps to prevent the mock from returning values for specific properties (e.g., when mocking a Promise, you might want to ignore then to avoid unexpected behavior).

    • configure(config: GlobalConfig): Performs a shallow merge of your config with the DEFAULT_CONFIG.
    • resetConfig(): Resets the global configuration to DEFAULT_CONFIG.
    • DEFAULT_CONFIG: Contains { ignoreProps: ['then'] } by default.
    import { JestMockExtended } from 'jest-mock-extended';
    
    JestMockExtended.configure({
      ignoreProps: ['then', 'someOtherProp']
    });
    
    // To revert to defaults
    JestMockExtended.resetConfig();
  11. Manage mock state with mockClear() and mockReset()

    master

    To maintain clean tests, you can manage the state of your mocks using these utility functions:

    • mockClear(mock): Clears the state of the mock (e.g., call history, instances) but keeps the implementation/return values intact.
    • mockReset(mock): Resets the mock to its initial state, clearing both the call history and any mocked implementations/return values.
    import { mockClear, mockReset } from 'jest-mock-extended';
    
    const myMock = mock<MyType>();
    
    // After some interactions:
    mockClear(myMock);
    // or
    mockReset(myMock);