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();
})
Related
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?
I did the tests in the following way, but it bothers me how I need to import an object with the functions, instead of just importing the functions.
this works
//service.test.js
const get = require('../../modules/bankUser/model/getRegisteredUser');
get.getRegisteredUser = jest.fn()
.mockImplementationOnce(async () => mock)
.mockImplementationOnce(async () => mockUpdated);
//service.js
const get = require('../model/getRegisteredUser');
const testedFunction () => {
const depositReciver = await get.getRegisteredUser(depositName, depositCpf);
}
this dosent
//service.test.js
const get = require('../../modules/bankUser/model/getRegisteredUser');
get.getRegisteredUser = jest.fn()
.mockImplementationOnce(async () => mock)
.mockImplementationOnce(async () => mockUpdated);
//service.js
const { getRegisteredUser } = require('../model/getRegisteredUser');
const testedFunction () => {
const depositReciver = await getRegisteredUser(depositName, depositCpf);
}
I'm triyng this (way):
//service.test.js
const get = require('../../modules/bankUser/model/getRegisteredUser');
jest.mock('../../modules/bankUser/model/getRegisteredUser');
get.mockImplementationOnce(() => ({ getRegisteredUser: () => mockObject }));
jest returns this:
TypeError: get.mockImplementationOnce is not a function
also I've tried to import like this
//service.test.js
const { getRegisteredUser } = require('../../modules/bankUser/model/getRegisteredUser');
jest.mock('../../modules/bankUser/model/getRegisteredUser');
getRegisteredUser.mockImplementationOnce(() => ({ getRegisteredUser: () => mockObject }));
EDIT
//getRegisteredUser.js
const { getConnection } = require('../../../global/connection');
const getRegisteredUser = async (userName, cpf) => {
const db = await getConnection('Data-Base');
const res = await db.collection('Collection')
.findOne({ userName, cpf });
return res;
};
module.exports = { getRegisteredUser };
The example you are pointing [https://jestjs.io/docs/es6-class-mocks#replacing-the-mock-using-mockimplementation-or-mockimplementationonce][1]
is related to the default export but I think you are using named export in your code.
There are multiple ways to do so
One Way
const get = require('../../modules/bankUser/model/getRegisteredUser');
jest.mock('../../modules/bankUser/model/getRegisteredUser');
get.getRegisteredUser.mockImplementationOnce(() => mockObject);
Second Way
jest.mock('../../modules/bankUser/model/getRegisteredUser', () => ({
...jest.requireActual('../../modules/bankUser/model/getRegisteredUser'),
getRegisteredUser: jest.fn().mockImplementation(() => mockObject),
}))
An Another Way
We can also mock a module using the __mocks__ directory which is inbuilt feature of jest
And at the end never forgot to call clearAllMocks() after each test else it may impact the output of other tests.
afterEach(() => {
jest.clearAllMocks();
});
I'm testing a cloud function named myCloudFn in my functions/send.js module. My tests are in functions/test/send.test.js:
// send.js
const { getCompareDate } = require('../utils.js');
async function myCloudFn(myTestDate) {
const compareDate = await getCompareDate(argToTest);
const isOlder = myTestDate < compareDate;
return isOlder ? 'older' : 'newer';
}
module.exports = { myCloudFn };
// send.test.js
const send = require('../send.js');
jest.mock('../utils', () => ({
getCompareDate: jest.fn(() => new Date('2020-01-31')) // default
.mockResolvedValueOnce(new Date('2020-04-04'))
.mockResolvedValueOnce(new Date('2020-02-02')),
}));
describe('send.js', () => {
it('returns date comparison from myCloudFn()', async () => {
const myTestDate = '2020-03-03';
const returnValues = ['older', 'newer'];
const responsePromises = returnValues.map(() => send.myCloudFn(myTestDate));
const responses = await Promise.all(responsePromises);
expect(responses[0]).toBe(returnValues[0]);
expect(responses[1]).toBe(returnValues[1]);
});
});
The test functions correctly and passes as expected when I mock getCompareDate in this way, but for flexibility, I would rather provide custom input values for getCompareDate inside my tests and not 'globally'. Here's what I've tried:
const mockGetCompareDate = jest.fn();
jest.mock('../utils', () => ({
getCompareDate: mockGetCompareDate,
}));
it('returns date comparison from myCloudFn()', async () => {
mockGetCompareDate
.mockResolvedValueOnce(new Date('2020-04-04'))
.mockResolvedValueOnce(new Date('2020-02-02'));
const myTestDate = '2020-03-03';
const returnValues = ['older', 'newer'];
const responsePromises = returnValues.map(() => send.myCloudFn(myTestDate));
const responses = await Promise.all(responsePromises);
expect(responses[0]).toBe(returnValues[0]);
expect(responses[1]).toBe(returnValues[1]);
});
This method, however, is not working and throws an error:
ReferenceError: Cannot access 'mockGetCompareDate' before initialization
I've used this method with other tests as noted in the solution in this question, but I am not seeing similar results here. What am I missing?
Jest is hoisting the mocked function to the top of the module, and hence throws this error. The mock should instead be used right before you run the test. Further reading.
Try this:
const { getCompareDate } = require('../utils.js');
const mockGetCompareDate = jest.fn(() => new Date('2020-01-31'));
jest.mock('../utils.js', () => ({
__esModule: true,
getCompareDate: jest.fn(),
default: jest.fn()
}));
beforeAll(() => {
getCompareDate.mockImplementation(mockGetCompareDate);
});
To provide custom values do as you did before, when initialising the mock function. Source
Like this:
const mockGetCompareDate = jest.fn()
.mockResolvedValueOnce(new Date('2020-04-04'))
.mockResolvedValueOnce(new Date('2020-02-02'));
Or do as you did before inside the test. Source
Like this:
it('returns date comparison from myCloudFn()', async () => {
mockGetCompareDate
.mockResolvedValueOnce(new Date('2020-04-04'))
.mockResolvedValueOnce(new Date('2020-02-02'));
So, this is actually something that happened to my several times and I never knew how to solve it.
I have a class like this
//myClass.js
const { get } = require('../models/myModel');
class MyClass {
constructor(id) {
this.id = id;
}
async getByID() {
return get(this.id);
}
}
and a controller that is
//controller.js
module.exports = MyClass;
const MyClass = require('./MyClass.js');
module.exports = {
getController: (req, res, next) => {
try {
const MyObject = new MyClass(1);
const info = await MyObject.getByID();
res.send(info)
}
catch (e) {
next(e);
}
}
}
When I want to do an E2E test, i need to mock that getByID to see what happen when it resolves and rejects.
So, how do I mock a constructor? I did something like this
// myclass.spec.js
const MyClass = require('./MyClass.js');
jest.mock('./MyClass', () => jest.fn().mockImplementation(() => ({
getByID: Promise.resolve({id: 1, name: 'John'}),
})));
describe('Testing MyClass', () => {
it('Should return info', () => {
const res = httpMocks.createResponse();
const mReq = httpMocks.createRequest();
const mNext = jest.fn();
const mRes = mockResponse();
await pipelineController(mReq, mRes, mNext);
expect(mRes.send).toHaveBeenCalledWith(1);
done();
})
})
I know this test currently works, but now I cant change that getByID mock value to see what happen when it rejects the promise.
If I try to include it inside the test (it) .... it won't mock anything...
I want something like
const MyClass = require('./MyClass.js');
const {pipelineController} = require('./controllers.js')
jest.mock('./MyClass', () => jest.fn().mockImplementation(() => ({
getInfo: jest.fn(),
})));
describe('Testing MyClass', () => {
it('Should return info', () => {
ProcessService.getInfo.mockImplementation(() => Promise.resolve('the value i want'))
const res = httpMocks.createResponse();
const mReq = httpMocks.createRequest();
const mNext = jest.fn();
const mRes = mockResponse();
await pipelineController(mReq, mRes, mNext);
expect(mRes.send).toHaveBeenCalledWith(1);
done();
})
it('Should return info', () => {
ProcessService.getInfo.mockImplementation(() => Promise.reject('the reason i want'))
const res = httpMocks.createResponse();
const mReq = httpMocks.createRequest();
const mNext = jest.fn();
const mRes = mockResponse();
await pipelineController(mReq, mRes, mNext);
expect(mRes.send).toHaveBeenCalledWith(1);
done();
})
})
Since getByID is called right after instantiation, a spy should be available outside mocked class in order to change the implementation.
This can be done with class auto-mock that works in obscure and unspecified ways but may be suitable for this specific case:
jest.mock('./MyClass.js')
const MyClass = require('./MyClass.js');
...
MyClass.prototype.getByID.mockResolvedValue({id: 1, name: 'John'});
// instantiate MyClass and call getByID
And this can be done by exposing a spy outside the class:
const mockGetByID = jest.fn();
jest.mock('../../../services/ProcessService', () => jest.fn(() => (
{ getByID: mockGetByID }
)));
const MyClass = require('./MyClass.js');
...
mockGetByID.mockResolvedValue({id: 1, name: 'John'});
// instantiate MyClass and call getByID
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()
})
})