Mock a function and used it as UUT with Jest in JS - javascript

I want to test one function from exported object in one describe and mock it in another function from the same exported object since the second function is using the first function.
// funcs.js
const func1 = () => {
return true;
};
const func2 = () => {
const bool = func1();
if (bool) {
// do something
} else {
// do else
}
};
export const funcs = {func1, func2};
// funcs.spec.js
const {funcs as uut} from './funcs';
describe('unit', () => {
describe('func 1', () => {
test('', () => {
const bool = uut.func1();
expect(bool).toBeTruthy();
});
});
describe('func 2', () => {
test('', () => {
jest.mock(uut.func1).mockReturnValue(false);
uut.func2();
// rest of test
});
});
});
I tried using jest.requireActual but did not work. What is the best approach for this if even possible?

Related

mocking private methods to test an exported method fails in jest

I'm having an actual class like this
my-file.js
const methodA = () => { return 'output-from-methodA'; }
const methodB = () => { const b = methodA(); b.c = "out-put bind"; return b; }
module.exports = {
methodB
}
my-file.test.js
const { methodA, methodB } = require('./my-file.js');
describe('methodB testing', () => {
it('should call methodA', () => {
methodB();
expect(methodA).toHaveBeenCalled()
}
});
here methodA is private method, so it is not explicit to the test file, then how i ensure it is called or not in the test files
There is no way to test the private function, only the alternative way found is to test the outputs like
const { methodA, methodB } = require('./my-file.js');
describe('methodB testing', () => {
it('should call methodA', () => {
const result = methodB();
expect(result.b.c).toBe("out-put bind")
}
});

Pass parameter to jest.mock

I have a mock as follows
jest.mock('react-hook-inview', () => {
const setRef = jest.fn();
const observer = jest.fn();
const intersecting = true;
const entry = {
y: 0,
};
return [setRef, intersecting, entry, observer];
});
Here I would like to change the intersecting value. How can I change that from one test to another? I was trying to use something like a factory:
const inView = (intersecting) => {
jest.mock('react-hook-inview', () => {
const setRef = jest.fn();
const observer = jest.fn();
const entry = {
y: 0,
};
return [setRef, intersecting, entry, observer];
});
}
and use it like
it('is invisible by default', () => {
const text = 'Test';
const { container } = render(<Reveal>{text}</Reveal>);
inView(false);
expect(container.firstChild).not.toBeVisible();
});
it('is visible when in view', () => {
const text = 'Test';
const { container } = render(<Reveal>{text}</Reveal>);
inView(true);
expect(container.firstChild).toBeVisible();
});
however this throws
The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables.
Invalid variable access: intersecting
does anybody have an idea?
cheers!
EDIT:
My solution for now is to mock it like this
import ReactHookInview from 'react-hook-inview';
jest.mock('react-hook-inview', () => ({
useInView: jest.fn().mockImplementation(() => {
const setRef = jest.fn();
const observer = jest.fn();
const intersecting = false;
const entry = {
boundingClientRect: {
y: 0,
},
};
return [setRef, intersecting, entry, observer];
}),
}));
and in my test I overwrite like this:
ReactHookInview.useInView.mockImplementation(() => {
const setRef = jest.fn();
const observer = jest.fn();
const intersecting = true;
const entry = {
boundingClientRect: {
y: 1,
},
};
return [setRef, intersecting, entry, observer];
});
but that's not really beautiful
You can mock your library as you have already done, import it and explicitly set it's value. And then before have two different sets of tests:
jest.mock('react-hook-inview', /* as above */ );
import rhi from 'react-hook-inview';
describe('my implementation', () => {
describe('while intersecting', () => {
let result;
beforeAll(() => {
rhi[1] = true;
result = myImplementationUsingIntersecting();
});
it('should result X', () => {
expect(result).toEqual(X);
});
});
describe('while NOT intersecting', () => {
let result;
beforeAll(() => {
rhi[1] = false;
result = myImplementationUsingIntersecting();
});
it.todo('should result Y');
});
})
working example
Edit 2: in order to properly mock a React hook
since the react hooks are functions that return stuff you'll can mock it like this
jest.mock('react-hook-inview', () => jest.fn());
// and you can take it's reference with import
import useInView from 'react-hook-inview';
// and then you can mock it's return value as array
beforeAll(() => {
useInView.mockReturnValue(['a', true, 'b', 'c']);
result = myImplementationUsingIntersecting();
})

Unit testing an inner function?

I have a function that has inner functions, for my unit test, I only want to test the functionality of the inner function, but when I export the function and call the inner function, npm tests returns an error.
In my main.js:
mainFunction = () => {
functionToBeTested = () => {
// some code
}
}
module.exports = {mainFunction: mainFunction}
In my test.js
const chai = require("chai");
const assert = require("chai").assert;
const mainFunction = require("./main");
describe ("test", () => {
it("returns results", () => {
let result = mainfunction.functionToBeTested(args);
//equal code
});
})
But when I run npm test, it says:
mainfunction.functionToBeTested is not a function.
What am I doing wrong?
If you want to chain your functions you can try something like that.
main.js
const mainFunction = () => {
const functionToBeTested = () => {
return "I got it";
}
return { functionToBeTested };
}
module.exports = { mainFunction };
test.js
const chai = require("chai");
const assert = require("chai").assert;
const mainFunction = require("./main");
const mf = mainFunction();
describe ("test", () => {
it("returns results", () => {
let result = mf.functionToBeTested(args);
//equal code
});
});
Actually, you can't call a function declare inside another function that way. A solution would be to declare functionToBeTested outside mainFunction, then call it :
main.js
const functionToBeTested = () => {
// some code
};
const mainFunction = () => {
functionToBeTested();
};
module.exports = { mainFunction, functionToBeTested }
test.js
const chai = require("chai");
const assert = require("chai").assert;
const { mainFunction, functionToBeTested } = require("./main");
describe ("test", () => {
it("tests mainFunction", () => {
let main = mainfunction(args);
...
});
it("tests functionToBeTested"), () => {
let tested = functionToBeTested(args);
...
});
})
It is because only mainFunction() is exported and not the functionToBeTested(), outside this module JS doesn't knows about the existence of the functionToBeTested().
I will recommend you to move functionToBeTested separated and export that as well or have a helper method for calling it.

Mocking complex module using Jest.js

This is what the module I want to mock looks like:
class TheModule {
constructor() {
this.subClass = new SubClass();
}
}
class SubClass {
constructor() {
this.someMethod = () => 'Did Something great';
}
}
module.exports = TheModule;
This is TheModule usage I want to test:
const TheModule = require('./TheModule');
const method = () => {
const theModule = new TheModule();
return theModule.subClass.someMethod();
}
module.exports = method;
This is my test:
describe('method test', () => {
it('should return "Test pass"', () => {
jest.doMock('./TheModule');
const theModuleMock = require('./TheModule');
const method = require('./TheModuleUsage');
const mock = () => 'Test pass';
theModuleMock.subClass.someMethod.mockImplementation(() => mock);
expect(method()).toEqual('Test pass');
});
});
When I run this test I get TypeError: Cannot read property 'someMethod' of undefined
Is it possible to mock this module without changing TheModule implementation?
If you will export SubClass you can mock it without changing TheModule but in your case you should mock SubClass property in TheModule explicitly with factory for example:
describe('method test', () => {
it('should return "Test pass"', () => {
let mockedSomeMethod = jest.fn().mockImplementation(() => 'Test pass');
jest.doMock('./TheModule', () => {
// mock constructor
return jest.fn().mockImplementation(() => {
return { subClass: { someMethod: mockedSomeMethod } }
});
});
const method = require('./TheModuleUsage');
expect(method()).toEqual('Test pass');
});
});

Jest mock module multiple times with different values

I have a function that I want to test and this function uses an imported module:
var a = require('./a');
function add(b) {
return a + b;
}
module.exports = add;
That a module returns a number in this sample, but in my real project I use that as a config object that is changed from time to time manually.
var a = 1;
module.exports = a;
The test for the add function looks like this:
describe('add', () => {
it('should add the mock number 1 to 2', () => {
jest.setMock('./a', 1);
const add = require('./add');
expect(add(2)).toBe(3);
});
it('should add the mock number 2 to 2', () => {
jest.setMock('./a', 2);
const add = require('./add');
expect(add(2)).toBe(4);
});
});
First test passes, The second test fails because it inherits from the first mock. Is there any way to mock the a module multiple times?
I would like a solution that doesn't imply refactoring the add function and instead focus on mocking that module multiple times. (in my real project that is a config file)
You can play around with the code here: https://repl.it/#adyz/NocturnalBadComma
Add
beforeEach(() => {
jest.resetModules();
});
Final tests
describe('add', () => {
beforeEach(() => {
jest.resetModules();
});
it('should add the mock number 5 to 2', () => {
jest.setMock('./a', 5);
const add = require('./add');
expect(add(2)).toBe(7);
});
it('should add the mock number 2 to 2', () => {
jest.setMock('./a', 2);
const add = require('./add');
expect(add(2)).toBe(4);
});
});
Demo: https://repl.it/repls/TrustingBelatedProprietarysoftware
To add to #Gigi's solution, I created another example, using jest.mock:
In the file multiplier.ts, multiplier is the exported function we want to test:
// file: multiplier.ts
import {getNumber} from './get-number'
const multiplier = (num:number) => num * getNumber()
export {multiplier}
In the file get-number.ts, getNumber is the module we want to mock:
// file: get-number.ts
const getNumber = () => 2
export {getNumber}
Here is the test:
// file: multiplier.test.ts
// import { multiplier } from "./multiplier" // <-- this will not work
describe("[multiplier]", () => {
beforeEach(() => {
jest.resetModules()
})
it('should mock getNumber so that getNumber return 3', () => {
const mockReturn = 3
jest.mock( './get-number', () => (
{ getNumber: jest.fn(()=>mockReturn) }
))
const { multiplier } = require('./multiplier')
expect(multiplier(2)).toBe(6)
})
it('should mock getNumber so that getNumber return 4', () => {
const mockReturn = 4
jest.mock( './get-number', () => (
{ getNumber: jest.fn(()=>mockReturn) }
))
const { multiplier } = require('./multiplier')
expect(multiplier(2)).toBe(8)
})
it('should mock getNumber so that getNumber return 5', () => {
const mockReturn = 5
jest.mock( './get-number', () => (
{ getNumber: jest.fn(()=>mockReturn) }
))
const { multiplier } = require('./multiplier')
expect(multiplier(2)).toBe(10)
})
})
Note: for this to work, we need to use require to import multiplier.ts
For callback functions, working approach is-
const { validate } = require("../../controllers/..")
describe('Party Authentication', () => {
afterEach(() => { jest.resetModules(); });
it('should return success response', async () => {
let req = {}
req['headers'] = {}
req['body'] = {}
jest.mock('../../config/database', () => ({
execute: (param1, param2, param3, callback) => callback(null, [{ return: 1, party_guid: "20090911093921694613", policy_guid: '20090911093921422222' }])
}));
const data = await validate(req)
expect(data.status).toBe(true)
expect(data).toHaveProperty('insuredGuid')
expect(data.insuredGuid).toBeTruthy()
expect(data).toHaveProperty('policyGuidAuthentication')
expect(data.policyGuidAuthentication).toBeTruthy()
})
it('should return response with error code and message', async () => {
let req = {}
req['headers'] = {}
req['body'] = {}
jest.mock('../../config/database', () => ({
execute: (param1, param2, param3, callback) => callback(null, [{ return: 0, error_message: "Segurado não foi localizado com a Guid informada", error_code: 5684 }])
}));
const data = await validate(req)
console.log("datadatadatadatadata", data)
expect(data.status).toBe(false)
expect(data).toHaveProperty('error')
expect(data.error).toBeTruthy()
})
})

Categories