How to mock a module just to call a function - javascript

Is it possible to make when someFunction or any inner function that call ReactDom.render use my function instead real implementation?
export const testHelperFunction = (widget, anotherArg) => {
jest.mock('react-dom', () => {
return {
__esModule: true,
render: console.log,
}});
widget.someFunction(anotherArg);
jest.unmock('react-dom');
};

Related

How to test functions in a function using Jest

I have some code that has functions inside functions, and I want to be able to unit test the functions inside the parent function.
I am looking to have tests that unit test these and spy on them (both requirements are needed).
Example:
export default parentFunction = () => {
const innerFunction = () => {
//that does stuff
}
const anotherInnerFunction = () => {
//that does more stuff
}
//and at some point, the functions are called
//like this
innerFunction()
const anotherFunction = () => {
//or like this
anotherInnerFunction()
}
}
I have not been able to find a way to test these inner functions. I have tried the following.
Example test
import parentFunction from "myfile"
it("should call innerFunction", () => {
//this causes an error in jest
const innerFunctionSpy = jest.spyOn(parentFunction, "innerFunction")
//..etc
expect(innerFunctionSpy).toHaveBeenCalled()
})
it("will return a value from anotherInnerFunction", () => {
//this does not work
const value = parentFunction.anotherInnerFunction()
//this also does not work
const value = parentFunction().anotherInnerFunction()
//..etc
})
Does the parent function need to be refactored in order to be able to tests these inner functions? If my parent function was an object then I could test these, however, I am not sure if I can refactor my code to work like this.
For example
export default parentFunction = {
innerFunction: () => {
//that does stuff
},
//more code
}
You cannot access the variables or functions scoped inside another function in JavaScript. Unless you explicitly expose them by returning them from that function or export them from the module. This is not about Jest, this is how it works in JavaScript.
jest.spyOn(parentFunction, "innerFunction")
The above line of code indicates to Jest that the innerFunction function is set as a property of the parentFunction object but that is not the case. In fact innerFunction is a function scoped inside the parentFunction which cannot be accessed from outside of the scope of parentFunction. Unless you return it explicitly or define it on the module level scope and then export it.
But the inner workings or the implementation details of such inner functions should not be exposed, but if it is needed it should be marked as such using an _ before its name, take the following example:
//scoped to the module
const _innerFunction = () => {
//that does stuff
}
//scoped to the module
const _anotherInnerFunction = () => {
//that does more stuff
}
//exported as a public API
const anotherFunction = () => {
_anotherInnerFunction()
}
const publicApi = {
anotherFunction,
// expose the private functions for unit tests
_innerFunction,
_anotherInnerFunction
}
export default publicApi;
Then in your Jest test case:
import publicApi from "myfile"
it("should call anotherFunction", () => {
const anotherFunctionSpy = jest.spyOn(publicApi, "anotherFunction")
//..etc
expect(anotherFunctionSpy ).toHaveBeenCalled()
})
it("should call _innerFunction", () => {
const innerFunctionSpy = jest.spyOn(publicApi, "_innerFunction")
//..etc
expect(innerFunctionSpy ).toHaveBeenCalled()
})

Using a function defined inside an exports function, from another file

I have some code like this in a file helperFunctions.js:
exports helperFunctions = () => {
const functionA = async(args) => {
console.log(args);
};
const functionB = async(args) => {
functionA(myArg);
};
}
How can I call functionA and functionB from a separate file altogether, say, main.js?
I've tried:
import { helperFunctions } from './helperFunctions';
//...some code
helperFunctions.functionA('hello');
// OR
functionA('hello');
The specific error is:
TypeError: _helperFunctions.helperFunctions.functionA is not a function
and when trying the second solution, it's:
ReferenceError: functionA is not defined
I'm trying to avoid importing literally every single function I'm using (by way of exporting every single function I'm using). I'd like to do something like helperFunctions.function for the functions I need.
It really need to be a function? You could export an Object:
// helperFunctions.js
let helperFunctions = {
functionA: async (args) => {
console.log(args);
},
functionB: async (args) => {
functionA(myArg);
}
}
exports helperFunctions;

Mock an import from another file but still return a mock value

I'm testing a function which calls another function imported from anotherFile. That outsideFunc returns an object which contains 'name'. I need this to exist in order to progress through the rest of my test/the function to work correctly.
systemUnderTest.js
import { outsideFunc } from './anotherFile.js';
function myFunc() {
const name = outsideFunc().name;
}
anotherFile.js:
export function outsideFunc() {
return { name : bob }
}
I don't care about testing anotherFile or the result of outsideFunc, but I still need to return a mock value as part of testing myFunc;
systemUnderTest.spec.js
describe("A situation", () => {
jest.mock("./anotherFile", () => ({
outsideFunc: jest.fn().mockReturnValue({
name: 'alice'
})
}));
it("Should continue through the function steps with no problems", () => {
expect(excludeCurrentProduct(initialState)).toBe('whatever Im testing');
});
});
The problem I get is that, when the unit test is working through myFunc, const name returns undefined where it should return alice. I would expect it to get the data from my jest.mock of the anotherFile file and its mock exported function, but it doesn't get the right response.
When I asset that I expect name = alice I actually get name = undefined.
systemUnderTest.js
import { outsideFunc } from './anotherFile.js';
// let's say that the function is exported
export function myFunc() {
const name = outsideFunc().name;
// and let's say that the function returns the name
return name;
}
you can describe in your
systemUnderTest.spec.js
import { myFunc } from './systemUnderTest';
import { outsideFunc } from './anotherFile';
// using auto-mocking has multiple advantages
// for example if the outsideFunc is deleted the test will fail
jest.mock('./anotherFile');
describe('myFunc', () => {
describe('if outsideFunc returns lemons', () => {
outsideFunc.mockReturnValue({name: 'lemons'});
it('should return lemons as well', () => {
expect(myFunc()).toEqual('lemons');
});
});
});
working example

Spy on a method of an object created dynamically during test execution

I need to spyOn a method of an object created dynamically inside another method
So consider the following:
public doStuff = () => {
const myThing = new MyThing();
myThing.doSomethingElse().then((data) => {
//do more stuff here...
})
}
I want to therefore spyOn the instance of MyThing and the call to doSomethingElse().
I did come across a solution here which make use of the object's prototype, which I attempted like so
spyOn(MyThing.prototype, 'doSomethingElse').and.returnValue(Promise.resolve({foo: 'bar'}));
But this does not work after I call doStuff() in my tests, I get an error:
Error: : doSomethingElse() method does not exist
But I know this method is fine, since it runs as expected locally.
I am unsure how to proceed, can anyone assist?
Thanks
You might be missing something in your test. This is a simple test and you can see it works as you expected
require("jasmine");
class MyThing {
async doSomethingElse() {
return { bar: "foo" };
}
}
class Stuff {
doStuff() {
const myThing = new MyThing();
myThing.doSomethingElse().then(data => {
console.log(data);
});
}
}
describe("doSomethingElse", () => {
it("toHaveBeenCalled", () => {
spyOn(MyThing.prototype, "doSomethingElse").and.returnValue(
Promise.resolve({ foo: "bar123" })
);
const stuff = new Stuff();
stuff.doStuff();
expect(MyThing.prototype.doSomethingElse).toHaveBeenCalled();
});
});
The key is to use the spyOn before you instantiate the class that contains the doStuff function. If we move the const stuff = new Stuff(); above the spyOn it fails.
Hope it helps
You can create a spy object with mock method by passing in an object where the property names represent returned data for methods.
describe('test', () => {
let mock;
beforeEach(() => {
mock = jasmine.createSpyObj('mock', {
doSomethingElse: Promise.resolve({foo: 'bar'})
});
});
it('call mock', async () => {
const result = await mock.doSomethingElse();
expect(result.foo).toEqual('bar');
});
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/jasmine.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/jasmine.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/jasmine-html.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/3.4.0/boot.js"></script>

How to create re-usable mocks in mocha

Say I want to create use sinon.stub to mock an object:
beforeEach(() => {
this.someMethod = stub(SomeObject, 'someMethod');
});
afterEach(() => {
this.someMethod.restore();
});
How can I move this code into a re-usable mock file so that I can include it in every test where the SomeObject needs to be mocked?
I ended up creating hooks to pass into my functions:
// SomeObjectMock
export function beforeEachHook(SomeObject) {
return function() {
this.someMethod = stub(SomeObject, 'someMethod');
}.bind(this);
}
export function afterEachHook() {
return function() {
this.someMethod.restore();
}.bind(this);
}
Then in my test file:
import {afterEachHook, beforeEachHook} from './SomeObjectMock';
describe('myTest', () => {
beforeEach(beforeEachHook.bind(this)(SomeObject));
afterEach(afterEachHook.bind(this)());
});

Categories

Resources