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
Bundle Uses Wrong Ruby Version
How to Get a Listing of Only Files Using Dir.Glob
How to Get Today's Date in Ruby 1.9.3
Ruby: "If !Object.Nil" or "If Object"
How to Set a Proxy in Rubys Net/Http
How to Upload a JSON File with Secret Keys to Heroku
No Such File to Load -- Bundler/Setup (Ruby on Rails)
Markdown to Plain Text in Ruby
Tinytds Error: Adaptive Server Connection Timed Out
Exclude Draft Articles from Solr Index with Sunspot
Using Ruby, What Is the Most Efficient Way to Get the Content Type of a Given Url
How to Run Perl and Ruby Scripts as Tasks in Ant
Creating a Gmail Draft with Recipients Through Gmail API
Installing MySQL-2.9.0 Gem on Windows Fails Due to Lack of Libmysql