Diff between exporting function as const and class method - 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".

Related

Mock a top level import of a class with an internal method I also need to mock with jest.fn() for a test- no __mocks__ [duplicate]

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 ;)

reset typescript singleton instance in each unit test

I have a typescript singleton class like so:
export default class MySingleton {
private constructor({
prop1,
prop2,
...
}: MySingletonConfig) {
this.prop1 = prop1 ?? 'defaultProp1';
this.prop2 = prop2;
this.prop3 = prop3 ?? 'defaultProp3';
/* ... some instruction ... */
MySingleton.instance = this;
}
static getInstance(params?: Configuration): MySingleton {
if (!this.instance && !params) {
throw MySingleton.instantiationError;
}
if (!this.instance) {
new MySingleton(params);
return this.instance;
}
return this.instance;
}
}
When I want to unit test it using jest, like so:
describe('getInstance()', () => {
test('it should return the same instance every time', () => {
const params = {
/* ... all the params ... */
};
const mySingleton = MySingleton.getInstance(params);
expect(MySingleton.getInstance()).toEqual(mySingleton);
});
test('it should return the instance with the default value', () => {
const params = {
/* ... ONLY THE REQUIRED PARAMS ... */
};
const mySingleton = MySingleton.getInstance(params);
expect(mySingleton.prop1).toEqual('defaultProp1');
expect(mySingleton.prop3).toEqual('defaultProp3');
});
});
This is failing, because we share the same instance between the 2 tests (as the singleton pattern work), therefore the second instantiation is useless.
Is there a way to reset/destroy the previous instantiation in order to properly check if those default values are properly setted with the second intantiation?
I don't see why you couldn't do:
MySingleton.instance = null;
const mySingleton = MySingleton.getInstance(params);
Ideally the istance property should be really private, but nobody prevents you from adding a reset() method on your class.
It is not particularly neat, as it would basically be for testing purposes only, but at least it would be more close to the canonical implementation of the singleton pattern.
That being said, I would carefully consider if using a singleton is a good idea. It might create a lot of headaches when unit testing your code.
Basically, the same problem you have here could present itself elsewhere when you try to test some code that makes use of your singleton.

Exporting multiple functions with arguments

All I am trying to do is to export two functions from a module. One function taking an argument and the other with no argument:
function initClass(params)
{
return new Promise( (resolve, reject) => {
if (!wallet) {
wallet = new WalletClient(params);
resolve(wallet);
} else {
console.log('Wallet is initialized');
resolve(wallet);
}
});
}
function getInstance()
{
return wallet;
}
For initClass(params) only, I can do this as:
module.exports = (params) => {
initClass(params)
}
And then I call this as:
var init = require('./class.js')(params).initClass(params);
This works fine.
Now, for me to export getInstance() as well, I have tried to do the following but it doesn't seem to work.
module.exports = (params) => {
initClass(params),
getInstance
}
This complaints that there is no function getInstance.
Then I tried this:
module.exports.init = (params) => {
initClass(params)
}
module.exports.instance = {
getInstance
}
Then call them as:
var init = require('./class.js').init(params).initClass(params);
What is the proper way to export multiple functions like this?
Thank you.
You're making it more complex than needed. Once you have your functions defined, you can export it with this:
module.exports = {
initClass,
getInstance
}
To use it, you do this:
const init = require("./class.js");
init.initClass(params);
const instance = init.getInstance();
What you're exporting from the module is an object (which I've named init in the example above) that contains two functions. You don't have to pass arguments to the functions at the time you require.
module.exports is basically an object with keys which can refer to any variables/functions of your file.In your case,
module.exports.initClass = function (params){
...
}
module.exports.getInstance = function (){
}
When importing it
var init = require('./class.') // init object has two keys -> initClass and getInstance
init.initClass('abc')
init.getInstance()
If i'm understanding correctly you are trying to export multiple methods if that is the case simple use this.
module.exports = {
method: function() {},
otherMethod: function(parmas) {}
}
In your code use like this.
var init = require('./class.js');
init.method()
init.otherMethond(paramObj)
If you want below scenario you need to check out about method chaining.
var init = require('./class.js').init(params).initClass(params);

Revealing Module Pattern Javascript

Which approach is more convenient in an real life project? Can someone please enlighten me? And tell the difference between all of them(efficiency / clean code etc.)
All of them give the same result. Classes are ES6.
2nd approach Constructor pattern combined with Revealing Module Pattern.
3rd is only a constructor pattern / function and the properties are used in an normal function declaration.
I want to make an application which i insert some data from the input form, creating with the classes / constructors and then display it in the UI. Data obviously is always changing( creating multiple objects when i insert new input form)
// 1st approach with classes
const test1 = (() => {
class TestClass {
constructor() {
this.string = 'Class';
}
classMethod() {
console.log(this.string);
}
}
const testClass = new TestClass();
return {
testClass
}
})();
test1.testClass.classMethod();
// 2nd approach with constructor and constructor protoype methods
const test2 = (() => {
function TestConstructor() {
this.string = 'Constructor';
}
TestConstructor.prototype = {
constructorMethod() {
console.log(this.string);
}
}
const testConstr = new TestConstructor();
return {
testConstr
}
})();
test2.testConstr.constructorMethod();
// 3rd approach with constructor with function declaration
const test3 = (() => {
function TestConstructor() {
this.string = 'Constructor with function declaration';
}
const testConstr = new TestConstructor();
function normal() {
console.log(testConstr.string);
}
return {
normal
}
})();
test3.normal();

module.exports that include all functions in a single line

This is a follow-up question to In Node.js, how do I "include" functions from my other files?
I would like to include an external js file that contains common functions for a node.js app.
From one of the answers in In Node.js, how do I "include" functions from my other files?, this can be done by
// tools.js
// ========
module.exports = {
foo: function () {
// whatever
},
bar: function () {
// whatever
}
};
var zemba = function () {
}
It is inconvenient to export each and every function. Is it possible to have a one-liner that exports all functions? Something that looks like this;
module.exports = 'all functions';
It is so much more convenient this way. It is also less buggy in case one forgets to export certain functions later.
If not a one-liner, are there simpler alternatives that make coding more convenient? I just want to include an external js file made up of common functions conveniently. Something like include <stdio.h> in C/C++.
You can write all your function declarations first and then export them in an object:
function bar() {
//bar
}
function foo() {
//foo
}
module.exports = {
foo, bar
};
There's no magical one-liner though, you need to explicitly export the functions you want to be public.
I have done something like the following:
var Exported = {
someFunction: function() { },
anotherFunction: function() { },
}
module.exports = Exported;
I require it in another file and I can access those functions
var Export = require('path/to/Exported');
Export.someFunction();
This is essentially just an object with functions in it, and then you export the object.
A really old question but I just had to solve the same issue myself.
the solution I used was to define a Class inside the module to contain all my functions and simply export an instance of the class.
classes.js looks like this:
class TestClass
{
Function1() {
return "Function1";
}
Function2() {
return "Function2";
}
}
module.exports = new TestClass();
app.js looks like this:
const TestClass = require("./classes");
console.log( TestClass.Function1);
just keep adding more functions to the class and they will be exported :)
It is worth noting that in ES6, you can now export functions like this:
export function foo(){}
export function bar(){}
function zemba(){}
Simply write export before the functions you want to export. More information here.
If you use ES6 you can do something like that:
function bar() {
//bar
}
function foo() {
//foo
}
export default { bar, foo };
const fs = require("fs")
var ExportAll = {
deleteFile : function deleteFile(image,folder="uploads"){
let imagePath = `public/${folder}/${image}`
if (fs.existsSync(imagePath)){
fs.unlinkSync(imagePath)
}
},
checkFile : function checkFile(image,folder="uploads"){
let imagePath = `public/${folder}/${image}`
if (fs.existsSync(imagePath)){
return true
}
else{
return false
}
},
rand : function(min=1,max=10)
{
return Math.floor((Math.random() * max) + min)
}
}
module.exports = ExportAll
Import everything from a type module file that has functions that are exported.
Found here:
https://javascript.info/import-export
myfile.js
export function myFunction()
{
// ................
}
export function myFunction2()
{
// ................
}
myfile2.js - import everything that is exported in the file
import * as myFunctions from './myfile.js';
// Usage
myFunctions.myFunction();
myFunctions.myFunction2();

Categories