How to Mock an Instance Method of an Already Mocked Object

How to mock an instance method of an already mocked object?

You could try something like this :-

user = Factory(:user)
user.stubs(:facebook => stub(:me => stub(:info => {:name => "John Doe"})))

If you really want to check that all these methods are called (which I suspect you don't), you could do the following :-

user = Factory(:user)
user.expects(:facebook => mock(:me => mock(:info => {:name => "John Doe"})))

It's a bit more verbose, but it's usually worthwhile giving each mock object a name :-

user = Factory(:user)
user.stubs(:facebook => stub('facebook', :me => stub('me', :info => {:name => "John Doe"})))

I hope that helps.

How to mock instance methods of a class mocked with jest.mock?

Automatic Mocking

Calling jest.mock automatically mocks all the exports from the module being mocked unless a manual mock is specified using the __mocks__ directory.

So, this line jest.mock("./Logger") has automatically replaced the Logger constructor and all of it's methods with mock functions allowing us to test how these functions behave.

And the information related to the instances created by Logger is saved in Logger.mock.instances, so we can use this to test if the methods are being called properly.

import Person from "./Person";
import Logger from "./Logger";

jest.mock("./Logger");

describe("Person", () => {
it("calls method1 on instantiation", () => {
const p = new Person();
// Logger constructor should have been called
expect(Logger).toHaveBeenCalled();

const mockLoggerInstance = Logger.mock.instances[0];
const mockMethod1 = mockLoggerInstance.method1;
// method1 should have also been called
expect(mockMethod1).toHaveBeenCalled();
});
});

Using Module Factory Parameter

You can also explicitly provide a module factory by passing in a factory function as the second argument to jest.mock. So, now the provided module factory would be used instead of Jest's automocking feature. Refer the docs for more information.

import Person from "./Person";
import Logger from "./Logger";

const mockMethod1 = jest.fn();
jest.mock("./Logger", () =>
jest.fn().mockImplementation(() => ({
method1: mockMethod1,
}))
);

describe("Person", () => {
it("calls method1 on instantiation", () => {
const p = new Person();
// Logger constructor should have been called
expect(Logger).toHaveBeenCalled();
// method1 should have also been called
expect(mockMethod1).toHaveBeenCalled();
});
});

Note: jest.mock() calls are hoisted, so you cannot first define a variable and then use it inside a factory function unless the variable is prefixed with mock. And because of this we can access mockMethod1 inside the factory.

Manual Mock

You can achieve a similar behavior to module factory function by creating a manual mock located at __mocks__/Logger.js. And now this mock implementation can be used across test files by simply calling jest.mock.

// __mocks__/Logger.js
const mockMethod1 = jest.fn();
const mockLogger = jest.fn(() => ({
method1: mockMethod1,
}));

Usage is similar to the module factory function but you now also have to import the mocked method in your test.

Note: You still need to use the original module path, don't include __mocks__.

import Person from "./Person";
import Logger, { mockMethod1 } from "./Logger";

jest.mock("./Logger");

describe("Person", () => {
it("calls method1 on instantiation", () => {
const p = new Person();
// Logger constructor should have been called
expect(Logger).toHaveBeenCalled();
// method1 should have also been called
expect(mockMethod1).toHaveBeenCalled();
});
});

Jest: How to mock one specific method of a class

Edit 05/03/2021

I see a number of people disagree with the below approach, and that's cool. I do have a slight disagreement with @blade's approach, though, in that it actually doesn't test the class because it's using mockImplementation. If the class changes, the tests will still always pass giving false positives. So here's an example with spyOn.

// person.js
export default class Person {
constructor(first, last) {
this.first = first;
this.last = last;
}
sayMyName() {
return this.first + " " + this.last; // Adjusted to return a value
}
bla() {
return "bla";
}
}

and the test:

import Person from './'

describe('Person class', () => {
const person = new Person('Guy', 'Smiley')

// Spying on the actual methods of the Person class
jest.spyOn(person, 'sayMyName')
jest.spyOn(person, 'bla')

it('should return out the first and last name', () => {
expect(person.sayMyName()).toEqual('Guy Smiley') // deterministic
expect(person.sayMyName).toHaveBeenCalledTimes(1)
});
it('should return bla when blah is called', () => {
expect(person.bla()).toEqual('bla')
expect(person.bla).toHaveBeenCalledTimes(1)
})
});

Cheers! /p>


I don't see how the mocked implementation actually solves anything for you. I think this makes a bit more sense

import Person from "./Person";

describe("Person", () => {
it("should...", () => {
const sayMyName = Person.prototype.sayMyName = jest.fn();
const person = new Person('guy', 'smiley');
const expected = {
first: 'guy',
last: 'smiley'
}

person.sayMyName();

expect(sayMyName).toHaveBeenCalledTimes(1);
expect(person).toEqual(expected);
});
});

How to mock a specific method of a class whilst keeping the implementation of all other methods with jest when the class instance isn't accessible?

You can use jest.spyOn and provide a mock implementation for method1.

// Logger.test.ts
import Logger from './Logger';

jest.spyOn(Logger.prototype, "method1").mockImplementation(() => "mocked")

describe("Logger", () => {
it("calls method1 & method2 but only method1 is mocked", () => {
const l = new Logger();
expect(l.method1()).toBe("mocked");
expect(l.method2()).toBe("method2");
})
})

But in case you have many methods and you want to mock each one of them except one single method, then you can get the original implementation of this one single method using jest.requireActual.

// Logger.test.ts
import Logger from "./Logger";

const mockMethod1 = jest.fn().mockReturnValue("mocked");
const mockMethod3 = jest.fn().mockReturnValue("mocked");
const mockMethod4 = jest.fn().mockReturnValue("mocked");
const mockMethod5 = jest.fn().mockReturnValue("mocked");
jest.mock("./Logger", () =>
jest.fn().mockImplementation(() => ({
method1: mockMethod1,
method2: jest.requireActual("./Logger").default.prototype.method2,
method3: mockMethod3,
method4: mockMethod4,
method5: mockMethod5,
}))
);

describe("Logger", () => {
it("calls all methods but only method1 is mocked", () => {
const l = new Logger();
expect(l.method1()).toBe("mocked");
expect(l.method2()).toBe("method2");
expect(l.method3()).toBe("mocked");
expect(l.method4()).toBe("mocked");
expect(l.method5()).toBe("mocked");
});
});

Note: You don't need to define an ES6 class for mocking, a constructor function also just works fine because ES6 classes are actually just syntactic sugar for constructor functions.

Mocking method in class that is already mocked

Fixed it by overriding the instance's foo's method

import unittest
from unittest.mock import patch

from foo import Beef

class TestBeef(unittest.TestCase):
@patch('foo.Foo')
def test_run(self, mock_foo):
beef = Beef()
beef.foo.bar.return_value = {'a': 'x'}
assert result == {'a': 'x'}

Can I add an instance method to a Python Mock object?

If you want to enhance the capabilities of the mock.Mock class, just subclass Mock and add your own methods.

class MyMock(Mock):
def session(self):
# Save session data here?

The mock documentation explains that when a new mock is to be created, it will be the same type as the parent. This means that the session function will also be available on any other mocks which are created during mocking.

This doesn't cover the case where you need to dynamically attach a session function to an existing mock object.



Related Topics



Leave a reply



Submit