js call function via closure vs call on es6 module - javascript

I have 2 functions defined like this:
export function builder(){ ... };
export function action() { return () => builder() };
Now I am trying to write a test that mock the builder function and return {};
import * as m from 'redux/modules/mymodule';
it('call buildSolrUrl', () => {
const spy = expect.spyOn(m, "builder").andReturn({});
m.action()();
expect(spy.calls.length).toEqual(1);
});
The problem is that builder does not get mock in that case.
If I change my code for:
export function action() { return () => this.builder() };
The method is mocked but my program does not work anymore because action() return a function that is executed later and this is not resolved to the right object.
To resume: My code works because the call to builder() is done via closure.
The tests are unable to mock the function that way because the function is called via the closure.
What is the best way to deal with this?

Related

In Jest, how to test a function declared inside a function?

I am trying to test a function that is declared inside another function(Parent). I tried a few things but was not able to test that. I am using react library and JEST testing framework in the project.
Here is the service file (service.ts) function which I am trying to test:
import { useThisHook } from './base-hooks';
export function someFunction(param: string) {
const [
resolveFunction,
,
{ loading, error },
] = useThisHook();
const childFun = (param : number) => {
resolveFunction(someArguments);
}
return{
someValue: childFun
}
}
Here is the spec file code:
import * as SomeHooks from './base-hooks';
import * as Service from './service';
describe('Service function', () => {
beforeEach(() => {
jest.spyOn(SomeHooks, 'useThisHook').mockReturnValue(
{
//Some Value
}
);
});
test('Some function test', () => {
const someFunctionResponse = Service.someFunction('string');
expect(someFunctionResponse).toEqual({
someValue: expect.any(Function),
});
});
});
Till here it is fine. Tests are also getting passed but the problem is that childFn is not getting the coverage and that's what my requirement is. I am new to React and Jest. I am not able to understand how can I achieve that. I tried many things but didn't succeed.
Your childFn is not being called, that's why it's not getting coverage.
You could either refactor the whole childFn to a new hook and test it individually with something like react-hooks-testing-library.
Or you could separate the childFn and declarate it outside the someFunction scope pass the resolveFunction and then test it.
You only get coverage if the code is actually called, not when it is declared.

Jest, mocking ES6 class constructor not working as intended

I have code that I am trying to test with Jest. I have an ES6 class, let's call it ClassA. Within my test file, I am mocking ClassA as follows:
const mockGetCheapestProductAllPages = jest.fn()
const ClassA = require('../src/ClassA/ClassA')
jest.mock('../src/ClassA/ClassA', () => {
return jest.fn().mockImplementation(() => {
return { getCheapestProductAllPages: mockGetCheapestProductAllPages }
})
})
This syntax works just fine for mocking the class. I am able to spy on getCheapestProductAllPages just fine, and mock return values from it, etc. The problem is, I cannot spy on the constructor for some reason. In the Jest documentation on mocking ES6 classes, it is implied that you can just reference the imported class in order to spy on the constructor, like so:
expect(ClassA).toHaveBeenCalled()
Unfortunately, this is not working for me. I know the constructor is being invoked in my test, but Jest tells me it is invoked 0 times. Any suggestions/anything look wrong? Tried to only share relevant code but please let me know if there is anything else you need to see. Thanks!
Ended up declaring my Jest mockImplementation outside of my mock and referencing it in my tests like so:
// top of test file
const mockGetCheapestProductAllPages = jest.fn()
const mockClassAImplementation = jest.fn().mockImplementation(() => {
return { getCheapestProductAllPages: mockGetCheapestProductAllPages }
})
const ClassA = require('../src/ClassA/ClassA')
jest.mock('../src/ClassA/ClassA', () => {
return mockClassAImplementation
})
// tests
test('Expect ClassA constructor to be called', () => {
// invoke method that invokes ClassA constructor
// this works!
expect(mockClassAImplementation).toHaveBeenCalled()
})

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()
})

Jest is not calling function correct number of times

I am mapping over an array and then calling an external function. example
['error1', 'error2'].map(err => myFunc())
then in my jest test I am saying, expect myFunc() to have been called 2 times
but it's saying it is only called once
it's an external function defined as such
export const myFunc = () => {}
then when I import im doing
import { myFunc } from 'file/path'
jest.mock('file/path', () => ({
myFunc: jest.fn(),
}))
expect(myFunc.mock.calls).toHaveLength(2)
anyone see what I'm doing wrong? really makes no sense as function should be called for every item in array :/

Mocking a function jest but jest calling original function

I have a function that returns true or false, lets call it myFunc
myFunc (){
if(something){return true}
else{return false}
}
that's what it does for sake of arg
I then call it somewhere else
if(myFunc()){
}else{
}
when I log it out, it continually comes out as false. however, when i have mocked it in my test like so:
const myMock = (myModule.myFunc = jest.fn())
myMock.mockReturnValue(true)
so why is it still coming back as false when I log it from the index file? or is that not quite how mocking works?
I'm guessing that myModule is the object you imported, and then you set the mock function on that object. But in the myModule file you are referencing that function directly, not through a module reference, right?
The proper way would probably be to move myFunc out of myModule. But if you want to keep it there, then you are going to have to partially mock myModule:
jest.mock('./myModule', () => {
return {
...jest.requireActual('./myModule'),
myFunc: jest.fn()
}
})
But seriously consider moving myFunc out of myModule, because partial mocking is difficult and confusing.
One way I found to solve my issue was to use a class instead.
Here is a sudo example:
Implementation
export class Location {
getLocation() {
const environment = this.getEnvironmentVariable();
return environment === "1" ? "USA" : "GLOBAL";
}
getEnvironmentVariable() {
return process.env.REACT_APP_LOCATION;
}
}
Test
import { Location } from "./config";
test('location', () => {
const config = new Location();
jest.spyOn(config, "getEnvironmentVariable").mockReturnValue("1")
const location = config.getLocation();
expect(location).toBe("USA");
});

Categories

Resources