Let's suppose I have the following class:
export default class Person {
constructor(first, last) {
this.first = first;
this.last = last;
}
sayMyName() {
console.log(this.first + " " + this.last);
}
bla() {
return "bla";
}
}
Suppose I want to create a mocked class where method 'sayMyName' will be mocked and method 'bla' will stay as is.
The test I wrote is:
const Person = require("../Person");
jest.mock('../Person', () => {
return jest.fn().mockImplementation(() => {
return {sayMyName: () => {
return 'Hello'
}};
});
});
let person = new Person();
test('MyTest', () => {
expect(person.sayMyName()).toBe("Hello");
expect(person.bla()).toBe("bla");
})
The first 'expect' statement passes, which means that 'sayMyName' was mocked successfully. But, the second 'expect' fails with the error:
TypeError: person.bla is not a function
I understand that the mocked class erased all methods.
I want to know how to mock a class such that only specific method(s) will be mocked.
Using jest.spyOn() is the proper Jest way of mocking a single method and leaving the rest be. Actually there are two slightly different approaches to this.
1. Modify the method only in a single object
import Person from "./Person";
test('Modify only instance', () => {
let person = new Person('Lorem', 'Ipsum');
let spy = jest.spyOn(person, 'sayMyName').mockImplementation(() => 'Hello');
expect(person.sayMyName()).toBe("Hello");
expect(person.bla()).toBe("bla");
// unnecessary in this case, putting it here just to illustrate how to "unmock" a method
spy.mockRestore();
});
2. Modify the class itself, so that all the instances are affected
import Person from "./Person";
beforeAll(() => {
jest.spyOn(Person.prototype, 'sayMyName').mockImplementation(() => 'Hello');
});
afterAll(() => {
jest.restoreAllMocks();
});
test('Modify class', () => {
let person = new Person('Lorem', 'Ipsum');
expect(person.sayMyName()).toBe("Hello");
expect(person.bla()).toBe("bla");
});
And for the sake of completeness, this is how you'd mock a static method:
jest.spyOn(Person, 'myStaticMethod').mockImplementation(() => 'blah');
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! 🍻
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);
});
});
Not really answer the question, but I want to show a use case where you want to mock a dependent class to verify another class.
For example: Foo depends on Bar. Internally Foo created an instance of Bar. You want to mock Bar for testing Foo.
Bar class
class Bar {
public runBar(): string {
return 'Real bar';
}
}
export default Bar;
Foo class
import Bar from './Bar';
class Foo {
private bar: Bar;
constructor() {
this.bar = new Bar();
}
public runFoo(): string {
return 'real foo : ' + this.bar.runBar();
}
}
export default Foo;
The test:
import Foo from './Foo';
import Bar from './Bar';
jest.mock('./Bar');
describe('Foo', () => {
it('should return correct foo', () => {
// As Bar is already mocked,
// we just need to cast it to jest.Mock (for TypeScript) and mock whatever you want
(Bar.prototype.runBar as jest.Mock).mockReturnValue('Mocked bar');
const foo = new Foo();
expect(foo.runFoo()).toBe('real foo : Mocked bar');
});
});
Note: this will not work if you use arrow functions to define methods in your class (as they are difference between instances). Converting it to regular instance method would make it work.
See also jest.requireActual(moduleName)
Have been asking similar question and I think figured out a solution. This should work no matter where Person class instance is actually used.
const Person = require("../Person");
jest.mock("../Person", function () {
const { default: mockRealPerson } = jest.requireActual('../Person');
mockRealPerson.prototype.sayMyName = function () {
return "Hello";
}
return mockRealPerson
});
test('MyTest', () => {
const person = new Person();
expect(person.sayMyName()).toBe("Hello");
expect(person.bla()).toBe("bla");
});
rather than mocking the class you could extend it like this:
class MockedPerson extends Person {
sayMyName () {
return 'Hello'
}
}
// and then
let person = new MockedPerson();
If you are using Typescript, you can do the following:
Person.prototype.sayMyName = jest.fn().mockImplementationOnce(async () =>
await 'my name is dev'
);
And in your test, you can do something like this:
const person = new Person();
const res = await person.sayMyName();
expect(res).toEqual('my name is dev');
Hope this helps someone!
I've combined both #sesamechicken and #Billy Reilly answers to create a util function that mock (one or more) specific methods of a class, without definitely impacting the class itself.
/**
* #CrazySynthax class, a tiny bit updated to be able to easily test the mock.
*/
class Person {
constructor(first, last) {
this.first = first;
this.last = last;
}
sayMyName() {
return this.first + " " + this.last + this.yourGodDamnRight();
}
yourGodDamnRight() {
return ", you're god damn right";
}
}
/**
* Return a new class, with some specific methods mocked.
*
* We have to create a new class in order to avoid altering the prototype of the class itself, which would
* most likely impact other tests.
*
* #param Klass: The class to mock
* #param functionNames: A string or a list of functions names to mock.
* #returns {Class} a new class.
*/
export function mockSpecificMethods(Klass, functionNames) {
if (!Array.isArray(functionNames))
functionNames = [functionNames];
class MockedKlass extends Klass {
}
const functionNamesLenght = functionNames.length;
for (let index = 0; index < functionNamesLenght; ++index) {
let name = functionNames[index];
MockedKlass.prototype[name] = jest.fn();
};
return MockedKlass;
}
/**
* Making sure it works
*/
describe('Specific Mocked function', () => {
it('mocking sayMyName', () => {
const walter = new (mockSpecificMethods(Person, 'yourGodDamnRight'))('walter', 'white');
walter.yourGodDamnRight.mockReturnValue(", that's correct"); // yourGodDamnRight is now a classic jest mock;
expect(walter.sayMyName()).toBe("walter white, that's correct");
expect(walter.yourGodDamnRight.mock.calls.length).toBe(1);
// assert that Person is not impacted.
const saul = new Person('saul', 'goodman');
expect(saul.sayMyName()).toBe("saul goodman, you're god damn right");
});
});
I was trying to get this to work on a class that had already been mocked. Because it had been mocked already, there was no prototype available for me to modify, so I found this workaround.
I don't love this solution, so if anyone knows a better way to update a method to a class that has already been mocked out, I'm all ears.
And just to clarify, the main answers to this question are working with classes that are not mocked out. In my situation, the class has already been mocked out and I'm trying to update one of the methods to the already-mocked class.
My solution:
const previousClassInstance = new PreviouslyMockedClass();
PreviouslyMockedClass.mockImplementation(() => {
return {
// "Import" the previous class methods at the top
...previousClassInstance,
// Then overwrite the ones you wanna update
myUpdatedMethod: jest.fn(() => {
console.log(
"This method is updated, the others are present and unaltered"
);
}),
};
});
I found a way to reproduce the original spyOn behaviour with Typescript and ES6 modules since you get a jest-Error nowadays when you try to use it on a class instance method.
const addTodoSpy = jest.spyOn(storeThatNeedsToBeSpiedOn, 'addTodo');
TypeError: Cannot redefine property: addTodo at Function.defineProperty (<anonymous>)
The advantage of spyOn is that the original method still runs in its original implementation.
In my case the class instance is a mobX store. But I see no reason why it shouldnt work for other class modules.
The way to do it is simply to save a copy of the original method, then creating a mock function with the saved copy as mockImplemtation and the saving this back into the class instance
const storeThatNeedsToBeSpiedOn = new TodoStore();
const keep = storeThatNeedsToBeSpiedOn.addTodo;
const addTodoSpy = jest.fn().mockImplementation(keep);
storeThatNeedsToBeSpiedOn.addTodo = addTodoSpy;
const storeToTest = new SomeOtherStore(storeThatNeedsToBeSpiedOn);
and in the test:
storeToTest.methodThatCallsAddTodoInternally();
expect(addTodoSpy).toBeCalledTimes(1);
the beauty of this is, that the original implementation of the method still runs with all its side effects (if there are any). So you could finish off your test by saying;
expect(storeThatNeedsToBeSpiedOn.todos.length).toEqual(/* one more than before */);
Hope this helps someone out there who is as frustrated as i was ;)
I want to access Main class methods to another Person class without creating a new instance Is it possible??
Can we access it without creating an instance of a class
let myInstance = new Person();
class Main {
constructor(args) {
this.hooks = [];
}
add_hooks(name, func) {
if (!this.hooks[name]) this.hooks[name] = [];
this.hooks[name].push(func);
}
call_hooks(name, ...params) {
if (this.hooks[name]) this.hooks[name].forEach((func) => func(...params));
}
}
other class Person how to access without using new keyword
const Main = require("./main.js");
class Person {
exec() {
const action = Main();
action.add_hook("jump", console.log.bind(console, "this will log "));
}
}
There is no big magic to it. Since the OP just wants to reuse prototypal Main methods, one is going to explicitly delegate the method/s of interest which was/were provided/accessed before via Main.prototype ...
class Main {
constructor(args) {
this.hooks = {};
}
add_hooks(name, func) {
if (!this.hooks[name]) {
this.hooks[name] = [];
}
this.hooks[name].push(func);
}
call_hooks(name, ...params) {
if (this.hooks[name]) {
this.hooks[name].forEach(func => func(...params));
}
}
}
// const Main = require("./main.js");
class Person {
// // ... either add `hooks` as public property at instantiation time ...
// hooks = {};
exec() {
const ref = Main.prototype;
ref.add_hooks.call(this, "jump", console.log.bind(console, "this will log"));
}
}
// ... or add `hooks` via additional glue code ...
function createPersonWithHooksAndExecute() {
const type = new Person();
type.hooks = {};
type.exec();
return type;
}
const someone = createPersonWithHooksAndExecute();
console.log({ someone });
// this will log
Main.prototype.call_hooks.call(someone, "jump");
.as-console-wrapper { min-height: 100%!important; top: 0; }
If you're not planning on instantiating the object, and you don't care about having multiple instances with each having their own state, you don't need a class.
Just create individual functions, or export an object.
const hooks = [];
export function add_hooks(name, func) {
if (!hooks[name]) hooks[name] = [];
hooks[name].push(func);
}
export function call_hooks(name, ...params) {
if (!hooks[name]) return;
for (const func of this.hooks[name]) {
func(...params);
}
}
It's possible too to do this with static methods, and that would be the likely answer if you write Java where everything has to be a class, but I wouldn't recommended it in Javascript.
Whats the diff between these two approaches of exporting and in which situations we go for exporting class and exporting function as const ? Which is ES6+ compatible?
// approach1.js
class Sample1 {
hello(visitorName) {
return `hello ${visitorName}`;
}
}
module.exports = Sample1;
And
// approach2.js
const hello = (visitorName) => {
return `hello ${visitorName}`;
};
module.exports = hello;
Test Class
// test.js
const Sample1 = require('./approach1');
const sample2 = require('./approach2');
async function start() {
const returnValueForApproach1 = (new Sample1()).hello('Name');
console.log(returnValueForApproach1);
const returnValueForApproach2 = sample2('Name');
console.log(returnValueForApproach2);
}
start();
Output:
hello Name
hello Name
You generally want to use a class when data gets stored on the instance - that is, when you need to refer to this in at least one of the methods. For example:
class Sample1 {
constructor(name) {
this.visitorName = name;
}
hello() {
return `hello ${this.visitorName}`;
}
}
const s = new Sample1('foobar');
// ... followed by some code, then
console.log(s.hello());
If you don't ever store data on the instance, using a class doesn't make a whole lot of sense, since it adds a bit of extra and somewhat confusing overhead without much reason. JavaScript isn't Java - don't feel like you have to tie everything to a class.
If you just have a single function, like in your example, then your second example of
const hello = (visitorName) => {
`hello ${visitorName}`;
};
module.exports = hello;
makes the most sense by far; just declare and export the function.
If you had a collection of functions not related to instance data, you could export an object with those functions, eg:
module.exports = {
fn1() {
// code
},
fn2() {
}
};
All of the code in the question and this answer uses ES6 syntax, so I suppose it's "ES6 compatible".
I have a CameraBuilder class that looks like this:
class CameraBuilder {
constructor() {
if (arguments.length) {
throw new Error('[CameraBuilder constructor ERROR] class constructor does not accept parameters.');
}
this.camera = {};
}
withFarmLabel(farmLabel) {
this.camera.farm_label = farmLabel;
return this;
}
// more methods here
build() {
const missingProps = [];
if (!this.camera.farm_label) {
missingProps.push('\nMissing farm_label property. Use the withFarmLabel method in order to assign it.');
}
// more validations like the one above here
if (missingProps.length) {
const errorMsg = missingProps.join('');
throw new Error(`[CameraBuilder build ERROR] ${errorMsg}`);
}
return this.camera;
}
}
Since most of my validations are on the build() method and there are some business logic on some of these methods associated with how the user is building an instance of CameraBuilder, I wouldn't want anyone assigning cameraBuilderObj.camera directly. Is there any way I can enforce the use of the Class methods in order to assign properties to the Camera object?
You could make the camera property private by putting # in front of it, ensuring that only CameraBuilder's internals can reference it:
class CameraBuilder {
#camera = {};
constructor() {
if (arguments.length) {
throw new Error('[CameraBuilder constructor ERROR] class constructor does not accept parameters.');
}
}
withFarmLabel(farmLabel) {
this.#camera.farm_label = farmLabel;
return this;
}
// more methods here
build() {
const missingProps = [];
if (!this.#camera.farm_label) {
missingProps.push('\nMissing farm_label property. Use the withFarmLabel method in order to assign it.');
}
// more validations like the one above here
if (missingProps.length) {
const errorMsg = missingProps.join('');
throw new Error(`[CameraBuilder build ERROR] ${errorMsg}`);
}
return this.#camera;
}
}
const c = new CameraBuilder();
c.withFarmLabel('label');
console.log(c.camera);
console.log(c.build().farm_label);
CertainPerformance's answer probably makes more sense--don't expose it in the first place--but if for some reason you didn't want to go that route (or if you're in an environment where private fields aren't supported) you could define setters on it, so that direct assignments go through your function.
class Foo {
constructor () {
this._bar = 'baz';
}
set bar (value) {
this._bar = value;
console.log('do whatever you want to do here.');
}
}
const f = new Foo();
f.bar = 'hey'; // direct assignment invokes the setter
// Class style
class class_sample {
constructor(){
}
get_something (key) {
result = do_something (key);
return result
}
}
let class_sample = new class_sample();
module.exports = class_sample;
// Object literal style
let literal_sample = {
get_something : () => {
result = do_something (key);
return result
}
}
module.exports = literal_sample;
let class_sample = require("/class_sample");
let literal_sample = require("literal_sample");
class_sample.get_something(key);
literal_sample.get_something(key);
I found there is a two type to make a module. The first is using Class, the second is Object literal.
Which one has better performance to make module?(about resource use and speed)