Ryan Peterson
Ryan Peterson

Reputation: 142

How to mock an encapsulated dependency with Jest?

I am trying to mock the return value of a method of a class which is instantiated inside of the class I am testing, without it persisting the mocked value across the rest of the tests.

Here is a very basic demo I've put together:

Mystery.ts

export class Mystery {
    private max = 10;

    public getNumber(): number {
        return Math.random() * this.max;
    }
}

TestedClass.ts

import { Mystery } from './Mystery';

export class TestedClass {
    public someMethod() {
        const numberGenerator = new Mystery();

        return numberGenerator.getNumber();
    }
}

TestedClass.test.ts

import { Mystery } from './Mystery';
import { TestedClass } from './TestedClass';

jest.mock('./Mystery');

describe('TestedClass', () => {
    beforeEach(jest.clearAllMocks);

    it('should return the number 1000', () => {
        // I want to have Mystery.getNumber() return 1000 for this test only.

        Mystery.prototype.getNumber = jest.fn().mockReturnValue(1000);
        // jest.spyOn(Mystery.prototype, 'getNumber').mockReturnValue(1000);
        // Mystery.prototype.getNumber = jest.fn().mockReturnValueOnce(1000);

        const provider = new TestedClass();
        const result = provider.someMethod();

        expect(result).toEqual(1000);
    });

    it('should return undefined', () => {
        const provider = new TestedClass();
        const result = provider.someMethod();

        // Expects: undefined, Received: 1000
        expect(result).toEqual(undefined);
    });
});

As you can see from the test, I am seeing 1000 appear in the second test which I would like to avoid. In the real scenario all of the tests will likely need to mock the return value of Mystery.getNumber() (in different ways) but I want to ensure that one test is not affecting another test as it could be now.

This current behaviour does make sense as I am changing the Mystery.prototype but I'm not sure how to mock the value for individual test in any other way. I am also aware of using composition, which would help tremendously here, but I would like to avoid this for the sake of this question.

Upvotes: 0

Views: 364

Answers (1)

Uchenna Iheanacho
Uchenna Iheanacho

Reputation: 166

If you're trying to mock the getNumber method for a single test case only and not for all test cases, the jest.fn().mockReturnValueOnce method should do the trick.

So instead of

Mystery.prototype.getNumber = jest.fn().mockReturnValue(1000);

use

Mystery.prototype.getNumber = jest.fn().mockReturnValueOnce(1000);

Upvotes: 1

Related Questions