If you want pass parameters into module you do it like this:
index.js
let test = require("./module.js")(something);
module.js
module.exports = (something) => {
//Working with `something` here
};
But is there so way how to do it without using module.exports = (var) => {}?
More specifficaly to be able to write for example code outside function container and then at the end of file do module.exports?
Or you are able to just do this?
const globalSomething;
(something) => {
globalSomething = something;
}
module.exports = heavyWorkWith(globalSomething);
If you understand me.
You aren't restricted to an unnamed function to pass around parameters, you can use a named function
export default function myExport(something) {
//...
}
or even a class
export default class myExport() {
constructor(something) {
//...
}
}
And on the inverse side, import:
const myExport = require('./myModuleOrWhatever');
const foo = new myExport(something);
Even moreso, you can just pass along module constants with your anonymous module export
const bar = somethingElse;
const baz = moreElse;
/*
alernatively
export default const bar = somethingElse
*/
module.exports = {
foo: (something) => {
//Working with `something` here
},
bar,
baz,
};
Related
What's the best way to correctly mock the following example?
The problem is that after import time, foo keeps the reference to the original unmocked bar.
module.js:
export function bar () {
return 'bar';
}
export function foo () {
return `I am foo. bar is ${bar()}`;
}
module.test.js:
import * as module from '../src/module';
describe('module', () => {
let barSpy;
beforeEach(() => {
barSpy = jest.spyOn(
module,
'bar'
).mockImplementation(jest.fn());
});
afterEach(() => {
barSpy.mockRestore();
});
it('foo', () => {
console.log(jest.isMockFunction(module.bar)); // outputs true
module.bar.mockReturnValue('fake bar');
console.log(module.bar()); // outputs 'fake bar';
expect(module.foo()).toEqual('I am foo. bar is fake bar');
/**
* does not work! we get the following:
*
* Expected value to equal:
* "I am foo. bar is fake bar"
* Received:
* "I am foo. bar is bar"
*/
});
});
I could change:
export function foo () {
return `I am foo. bar is ${bar()}`;
}
to:
export function foo () {
return `I am foo. bar is ${exports.bar()}`;
}
but this is pretty ugly in my opinion to do everywhere.
An alternative solution can be importing the module into its own code file and using the imported instance of all of the exported entities. Like this:
import * as thisModule from './module';
export function bar () {
return 'bar';
}
export function foo () {
return `I am foo. bar is ${thisModule.bar()}`;
}
Now mocking bar is really easy, because foo is also using the exported instance of bar:
import * as module from '../src/module';
describe('module', () => {
it('foo', () => {
spyOn(module, 'bar').and.returnValue('fake bar');
expect(module.foo()).toEqual('I am foo. bar is fake bar');
});
});
Importing the module into its own code looks strange, but due to the ES6's support for cyclic imports, it works really smoothly.
The problem seems to be related to how you expect the scope of bar to be resolved.
On one hand, in module.js you export two functions (instead of an object holding these two functions). Because of the way modules are exported the reference to the container of the exported things is exports like you mentioned it.
On the other hand, you handle your export (that you aliased module) like an object holding these functions and trying to replace one of its function (the function bar).
If you look closely at your foo implementation you are actually holding a fixed reference to the bar function.
When you think you replaced the bar function with a new one you just actually replaced the reference copy in the scope of your module.test.js
To make foo actually use another version of bar you have two possibilities :
In module.js export a class or an instance, holding both the foo and bar method:
Module.js:
export class MyModule {
function bar () {
return 'bar';
}
function foo () {
return `I am foo. bar is ${this.bar()}`;
}
}
Note the use of this keyword in the foo method.
Module.test.js:
import { MyModule } from '../src/module'
describe('MyModule', () => {
//System under test :
const sut:MyModule = new MyModule();
let barSpy;
beforeEach(() => {
barSpy = jest.spyOn(
sut,
'bar'
).mockImplementation(jest.fn());
});
afterEach(() => {
barSpy.mockRestore();
});
it('foo', () => {
sut.bar.mockReturnValue('fake bar');
expect(sut.foo()).toEqual('I am foo. bar is fake bar');
});
});
Like you said, rewrite the global reference in the global exports container. This is not a recommended way to go as you will possibly introduce weird behaviors in other tests if you don't properly reset the exports to its initial state.
fwiw, the solution I settled on was to use dependency injection, by setting a default argument.
So I would change
export function bar () {
return 'bar';
}
export function foo () {
return `I am foo. bar is ${bar()}`;
}
to
export function bar () {
return 'bar';
}
export function foo (_bar = bar) {
return `I am foo. bar is ${_bar()}`;
}
This is not a breaking change to the API of my component, and I can easily override bar in my test by doing the following
import { foo, bar } from '../src/module';
describe('module', () => {
it('foo', () => {
const dummyBar = jest.fn().mockReturnValue('fake bar');
expect(foo(dummyBar)).toEqual('I am foo. bar is fake bar');
});
});
This has the benefit of leading to slightly nicer test code too :)
I had this same problem and due to the project's linting standards, defining a class or rewriting references in the exports were not code review approvable options even if not prevented by the linting definitions. What I stumbled on as a viable option is to use the babel-rewire-plugin which is much cleaner, at least in appearance. While I found this used in another project I had access to, I noticed it was already in an answer in a similar question which I have linked here. This is a snippet adjusted for this question (and without using spies) provided from the linked answer for reference (I also added semicolons in addition to removing spies because I'm not a heathen):
import __RewireAPI__, * as module from '../module';
describe('foo', () => {
it('calls bar', () => {
const barMock = jest.fn();
__RewireAPI__.__Rewire__('bar', barMock);
module.foo();
expect(bar).toHaveBeenCalledTimes(1);
});
});
https://stackoverflow.com/a/45645229/6867420
Works for me:
cat moduleWithFunc.ts
export function funcA() {
return export.funcB();
}
export function funcB() {
return false;
}
cat moduleWithFunc.test.ts
import * as module from './moduleWithFunc';
describe('testFunc', () => {
beforeEach(() => {
jest.clearAllMocks();
});
afterEach(() => {
module.funcB.mockRestore();
});
it.only('testCase', () => {
// arrange
jest.spyOn(module, 'funcB').mockImplementationOnce(jest.fn().mockReturnValue(true));
// act
const result = module.funcA();
// assert
expect(result).toEqual(true);
expect(module.funcB).toHaveBeenCalledTimes(1);
});
});
From this thread:
Try using a function expression
export const bar = () => {
return "bar"
}
This should let you spy on bar even if its used by another function in the same module.
If you define your exports you can then reference your functions as part of the exports object. Then you can overwrite the functions in your mocks individually. This is due to how the import works as a reference, not a copy.
module.js:
exports.bar () => {
return 'bar';
}
exports.foo () => {
return `I am foo. bar is ${exports.bar()}`;
}
module.test.js:
describe('MyModule', () => {
it('foo', () => {
let module = require('./module')
module.bar = jest.fn(()=>{return 'fake bar'})
expect(module.foo()).toEqual('I am foo. bar is fake bar');
});
})
If you're using Babel (i.e. #babel/parser) to handle transpiling your code, the babel-plugin-explicit-exports-references1 npm package solves this pretty elegantly by making the "ugly" module.exports replacements for you transparently at transpile time. See the original problem thread for more information.
1 Note: I wrote this plugin!
For CommonJS modules users, suppose the file looks something like:
/* myModule.js */
function bar() {
return "bar";
}
function foo() {
return `I am foo. bar is ${bar()}`;
}
module.exports = { bar, foo };
You need to modify the file to:
/* myModule.js */
function bar() {
return "bar";
}
function foo() {
return `I am foo. bar is ${myModule.bar()}`; // Change `bar()` to `myModule.bar()`
}
const myModule = { bar, foo }; // Items you wish to export
module.exports = myModule; // Export the object
Your original test suite (myModule.test.js) should now pass:
const myModule = require("./myModule");
describe("myModule", () => {
test("foo", () => {
jest.spyOn(myModule, "bar").mockReturnValueOnce("bar-mock");
const result = myModule.foo();
expect(result).toBe("I am foo. bar is bar-mock");
});
});
Read more: Mock/Spy exported functions within a single module in Jest
There are various hacks available here to make this work, but the real answer most people should be using is: don't. Taking the OP's example module:
export function bar () {
return 'bar';
}
export function foo () {
return `I am foo. bar is ${bar()}`;
}
and testing the actual behaviour, you'd write:
import { bar, foo } from "path/to/module";
describe("module", () => {
it("foo returns 'bar'", () => {
expect(bar()).toBe('bar');
});
it("foo returns 'I am foo. bar is bar'", () => {
expect(foo()).toBe('I am foo. bar is bar');
});
});
Why? Because then you can refactor inside the module boundary without changing the tests, which gives you the confidence to improve the quality of your code in the knowledge that it still does what it's supposed to.
Imagine you extracted the creation of 'bar' from bar to an unexported function, for example:
function rawBar() {
return 'bar';
}
export function bar () {
return rawBar();
}
export function foo () {
return `I am foo. bar is ${rawBar()}`;
}
The test I suggest above would pass. If you'd asserted that calling foo meant bar got called, that test would start failing, even though the refactor preserved the module's behaviour (same API, same outputs). That's an implementation detail.
Test doubles are for collaborators, if something really does need to be mocked here it should be extracted to a separate module (then mocking it is much easier, which tells you you're moving in the right direction). Trying to mock functions in the same module is like mocking parts of a class you're trying to test, which I illustrate similarly here: https://stackoverflow.com/a/66752334/3001761.
I've been stuck on this issue for a while. I cannot describe it accurately enough to find solutions online - apologies if it is a duplicate question.
I want to access helloWorld() from module.js:
export function HelperProvider() {
return class Helper {
constructor() {
}
helloWorld() {
console.log('Hello World');
}
}
}
In another file:
import { HelperProvider } from 'module.js'
const helperProvider = HelperProvider;
const helper = new helperProvider();
helper.helloWorld();
However, I encounter the following error:
Uncaught TypeError: helper.helloWorld is not a function
Any help would be very much appreciated.
You need to invoke the function HelperProvider to get the class.
const helperProvider = HelperProvider();
function HelperProvider() {
return class Helper {
constructor() {
}
helloWorld() {
console.log('Hello World');
}
}
}
const helperProvider = HelperProvider();
const helper = new helperProvider();
helper.helloWorld();
You are using module features that's not out of the box in nodejs, if you want to use modules you'll need to set type: "module" in the package.json file... see details
If you wanna use node ways:
module.js
function HelperProvider() {
return class Helper {
constructor() {}
helloWorld() {
console.log("Hello World");
}
};
}
module.exports = HelperProvider;
index.js
const HelperProvider = require("./Helper");
const helperProvider = HelperProvider();
const helper = new helperProvider();
helper.helloWorld();
When using a named export to return an object literal composed of functions, is it possible to pass a parameter to one of those functions?
For example, let's say the function below returns conditional results depending on if user's an admin:
// gridConfig.js
function getColumnDefs(isAdmin = false) {
// conditionally return columns
return {
orders: [ ... ],
...
}
}
export const config = {
columnDefs: getColumnDefs(),
rowDefs: getRowDefs(),
...
};
// main.js
import { config } from './gridConfig';
function doStuff() {
const { columnDefs, rowDefs } = config;
grid.columnDefs = columnDefs['orders'];
...
}
If I add the parameter to the function call inside the export, it says the param isn't defined. Adding a parameter to the export alias gives syntax errors. Even if it allowed this, I'm not clear where I'd pass my param inside main.js.
Is there some way of passing a parameter when structuring an export in this manner?
Maybe keeping it simple can be useful :)
export const config = (isAdmin) => ({
columnDefs: getColumnDefs(isAdmin),
rowDefs: getRowDefs(),
...
});
// Import
import { config } from '[...]'; // Placeholder path of import
const myConfigFalse = config(false);
const myConfigTrue = config(true);
export const config = admin => ({
columnDefs: getColumnDefs(admin),
rowDefs: getRowDefs(),
});
// main.js
import { config } from './gridConfig';
function doStuff() {
const { columnDefs, rowDefs } = config(admin);//get the admin variable set before this line
grid.columnDefs = columnDefs['orders'];
...
}
I am familiar with setting spies on Class or Object methods, but what about when the function is just an export default - such that the method itself is independent, like a utility?
I have some existing code like so:
const Funct1 = props => {
if(props){
Funct2(args);
}
// or return something
};
const Funct2 = props => {
// do something
return true
};
export default Funct1; //Yes the existing export is named the same as the "entry" method above.
And, for example, I'd like to spy on Funct1 getting called and Funct2 returns true.
import Funct1 from "../../../src/components/Funct1";
describe("Test the Thing", () => {
it("New Test", () => {
let props = {
active: true,
agentStatus: "online"
};
const spy = spyOn(Funct2, "method name"); <-- how doe this work if not an obj or class?
Funct1(props);
//If I try Funct2(props) instead, terminal output is "Funct2 is not defined"
expect(spy).toHaveBeenCalledWith(props);
});
});
I am not expert in jest, but my recommendation to think about:
1) When the function is exported as default I use something like:
import Funct1 from "../../../src/components/Funct1";
...
jest.mock("../../../src/components/Funct1");
...
expect(Funct1).toHaveBeenCalledWith(params);
2) When the module (utils.js) has multiple exports as
export const f1 = () => {};
...
export const f8 = () => {};
You can try
import * as Utils from "../../../src/components/utils"
const f8Spy = jest.spyOn(Utils, 'f8');
...
expect(f8Spy).toHaveBeenCalledWith(params);
Similar discussion here
Wrap your function with jest.fn. Like this:
const simpleFn = (arg) => arg;
const simpleFnSpy = jest.fn(simpleFn);
simpleFnSpy(1);
expect(simpleFnSpy).toBeCalledWith(1); // Passes test
I think Jest requires mocks to be inside an Object or Class, but if they aren't like that in your code, you can still put them there in the test:
jest.mock('../pathToUtils', () => ({
func2: jest.fun().mockImplementation((props) => "ok") //return what you want
otherfuncs: ...etc
}));
const mockUtils = require('../pathToUtils')
const func1 = require('./pathToFunc1')
Then in the test:
func1() // call the main function
expect(mockUtils.func2).toHaveBeenCalled
expect(mockUtils.func2).toHaveBeenCalledTimes(1)
expect(mockUtils.func2).toHaveBeenCalledWith(arg1, arg2, etc)
expect(mockUtils.func2).toHaveBeenLastCalledWith(arg1, arg2, etc)
You'll have already done unit testing of the Util function in another file, so you won't actually be running the function again here, this is just mocking for the purpose of the integration testing.
I believe that is not possible to test Funct1 calling Funct2 without modifying the existing code. If the latter is an option, here are two options to introduce dependency injection:
Create and export a factory function:
const Funct2 = props => {
// do something
return true;
};
const Funct1 = CreateFunct1(Funct2);
export function CreateFunct1(Funct2) {
return props => {
if (props) {
Funct2(props);
}
// or return something
};
}
export default Funct1;
// and here is the test:
describe('Test the Thing', () => {
it('New Test', () => {
// Arrange
const funct2Spy = jasmine.createSpy('Funct2');
const funct1 = CreateFunct1(funct2Spy);
const props = "some data";
// Act
funct1(props);
// Assert
expect(funct2Spy).toHaveBeenCalledWith(props);
});
});
Export Funct2 too. Here is a thread on this topic. Maybe its example would have to be tweaked a little bit for your scenario because of the export syntax.
What is the syntax to export a function from a module in Node.js?
function foo() {}
function bar() {}
export foo; // I don't think this is valid?
export default bar;
In Node you export things with module.exports special object. For example:
This exports both functions:
module.exports = { foo, bar };
They can be used as:
const { foo, bar } = require('./module/path');
To export one of those functions as top-level object you can use:
module.exports = foo;
module.exports.bar = bar;
which can be used as:
const foo = require('./module/path');
and:
const { bar } = require('./module/path');
or:
const foo = require('./module/path');
const { bar } = foo;
or:
const foo = require('./module/path');
const bar = foo.bar;
etc.
This is "the syntax to export a function from a module in Node.js" as asked in the question - i.e. the syntax that is natively supported by Node. Node doesn't support import/export syntax (see this to know why). As slezica pointed put in the comments below you can use a transpiler like Babel to convert the import/export keywords to syntax understood by Node.
See those answers for more info:
Is it ok to use Babel npm package for node.js server application
javascript - Why is there a spec for sync and async modules?
to expose both foo and bar functions:
module.exports = {
foo: function() {},
bar: function() {}
}
You can also do this in a shorter form
// people.js
function Foo() {
// ...
}
function Bar() {
// ...
}
module.exports = { Foo, Bar}
Importing:
// index.js
const { Foo, Bar } = require('./people.js');
export function foo(){...};
Or, if the function has been declared earlier:
export {foo};
Reference: MDN export