jest mocking functions without passing as a callback - node.js

I have some code like:
module.exports = {
idCheck: function(errors) {
errors.some( (error) => {
if (error.parentSchema.regexp === '/^((?!\\bMyId\\b).)*$/i') {
this._recordError('IDCHECK');
}
});
}
};
I am trying to test it using jest with this:
const IDCheck = require(
'./IDCheck'
);
let errors = [
{
parentSchema: {
regexp: '/^((?!\\bMyId\\b).)*$/i'
}
}
];
describe('IDCheck', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('calls _recordError with IDCHECK', () => {
jest.spyOn(this, '_recordError');
IDCheck.idCheck(errors);
});
});
however, when running jest, I get
Cannot spy the _recordError property because it is not a function; undefined given instead
Is there a way of mocking, testing for _recordError() having been called, or not called and with the correct parameter, without passing _recordError through as a parameter?

A few things about this line: jest.spyOn(this, '_recordError');
this has to be IDCheck because there is no this in scope since you are using arrow functions that inherit this if previously set (which it isn't). You can console.log(this) right above the line to prove that point.
'_recordError' is not a method of IDCheck. spyOn checks the target's methods, not methods called within it. Now if _recordError is a method of IDCheck, then you should be ok.
Finally, you basically have to return the data you want in order to verify it. There's no real way to check what was passed unless you return it.
Here's a solution I came up with that does not include some fixes you'd have to implement to fix the potential workflow flaws.
const IDCheck = {
idCheck: function(errors) {
return errors.map(error => {
if (error.parentSchema.regexp === '/^((?!\\bMyId\\b).)*$/i') {
return this._recordError('IDCHECK')
}
})
},
_recordError: function(data) {
return data
}
}
let errors = [
{
parentSchema: {
regexp: '/^((?!\\bMyId\\b).)*$/i'
}
}
];
describe('IDCheck', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('calls _recordError with IDCHECK', () => {
const spy = jest.spyOn(IDCheck, '_recordError')
const check = IDCheck.idCheck(errors).includes('IDCHECK')
expect(spy).toHaveBeenCalled()
expect(check).toBe(true)
});
});

Related

Unmock function after mockimplementation

I'm having a bit of trouble unmocking a function.
I first mock it and now I can't unmock it
//myClass.js
class myClass {
static check(v1,v2) {
return v1 > v2;
}
static async getinfo(v1,v2) {
if (this.check(v1,v2)) {
return await get('api.google.com');
}
return [];
}
}
//myclass.spec.js
describe('Testing myClass', () => {
describe('testing processing', () => {
it('should return result', () => {
const mockPatch = jest.fn().mockImplementation((version, solution) => false);
myClass.check = mockCheck;
try {
const result = await myClass.getinfo(1,2);
expect(result).toBe.([]);
}catch(e) {
throw e;
}
})
})
describe('Testing check', () => {
it('should return true', () => {
expect(myClass.check(2,1)).toBe.true
})
})
})
I already try with
myClass.check.mockRestore()
beforeEach(() => {myClass.check.mockRestore()})
jest.unmock('./myClass.js)
Is there anyway I can solve this? I read all the jest doc and i couldn't find anything
Methods should never be mocked by reassigning them, there is no way how Jest could restore their original implementation this way.
This should always be done with spyOn:
jest.spyOn(myClass, 'check').mockReturnValue(false)
This way a method can be restored with restoreMock or restoreAllMocks. This should be preferably enabled globally in Jest configuration.
I'm assuming that what you're hoping to do is to mock an implementation for use in a specific test, but then have your other tests function without the mocking.
If so, I think you could use the module mocking strategy in conjunction with mockReturnValueOnce.
Be sure to import your module at the top of your tests, then to call jest.mock with the same path. After that, you should be able to call myClass.check.mockReturnValueOnce, and it will be mocked until the next time it is called. After that, it will function normally 👍

Testing a controller API that refirects

I have a controller in Nest.JS that redirects:
#Get('route/:value')
async route(#Param('value') value: string) {
const result = await this.someService.doSomethingWithValue(value);
if (result) {
return { url: 'http://example.com/success.html' };
} else {
return { url: 'http://example.com/fail.html' };
}
}
How can I test in controller.spec.ts the correct redirect response?
i.e.:
describe('test', () => {
it('should show success page', async() => {
service.doSomethingWithValue = jest.fn(() => Promise.resolve(true));
expect(controller.route('value')).toBe(?????);
});
});
It should be exactly what you expect it to be. You are returning values, it should be those values, right? Actually those objects, so you should use toEqual instead of toBe. In this case, you're returning true so you should have expect(controller.route('value')).toEqual({ url: 'http://example.com/success.html' }). Start using return types on everything you can, it'll help you in the long run.

Is it possible to use the Node testing library Rewire to mock two calls to the same function so they return different results?

I'm new to mocking in Node. I'm using the library Rewire which seems to be one of the better options. I've run into a issue where I need to mock the same function twice within a method so that they return different results:
const tourId: string = await redis.read(accessToken, false);
if (tourId === null) {
logger.warn('invalid token', { accessToken });
throw boom.notFound(codes.RECORD_NOT_FOUND);
}
logger.warn('found an access token', { tourId });
const tourResponse: string = await redis.read(tourId, false);
if (tourResponse === null) {
logger.warn('tour not found', { accessToken, tourId });
throw boom.notFound(codes.RECORD_NOT_FOUND);
}
In the above example, I call the function redis.read() twice. In order to properly get the test coverage I want I would need to mock those two calls differently which I'm struggling with. Here is a test case I have so far:
it('returns a 404 error if access token not found in redis', async () => {
service.__set__({
redis: {
read: (accessToken: string): string | null => null,
},
});
service.getHandler('12345').catch((result: boom) => {
expect(result.isBoom, 'should be boom error').to.be.true;
expect(result.output.payload.statusCode, 'should be 404 error').to.equal(404);
expect(result.output.payload.message, 'should be RECORD_NOT_FOUND error').to.equal(codes.RECORD_NOT_FOUND);
});
});
Any tips are appreciated!
Answering my own question here: sinon.stub() has an onCall() method you can use to change the output for the :nth call.
https://sinonjs.org/releases/latest/stubs/#stuboncalln-added-in-v18
it('returns an iTour instance if found in redis', async () => {
const callback = sinon.stub();
callback.onCall(0).returns('12345');
callback.onCall(1).returns('67890');
service.__set__({
redis: {
read: () => callback(),
}
});
service.getHandler('12345').catch((result: boom) => {
expect(result.isBoom, 'should be boom error').to.be.true;
expect(result.output.payload.statusCode, 'should be 404 error').to.equal(404);
expect(result.output.payload.message, 'should be RECORD_NOT_FOUND error').to.equal(codes.RECORD_NOT_FOUND);
});
});

Accessing .mock property of an automocked function

I have this code:
import * as a from 'a-a';
jest.mock('a-a');
describe('a-a', () => {
beforeAll(async () => {
const x = await a.x(1); // Calls the mock
console.log(x); // 1
console.log(a.x.mock) // Undefined
});
});
The mock function is:
export async function x(data) {
cache.push(data);
console.log('HERE'); // this is printed
return data;
}
The mock of the module is in the __mocks__ directory.
The a.x() calls the mocked function, but a.x.mock is undefined.
How is that possible? Where is the .mock property?
So, after some investigation I found out that the functions declared in the __mocks__ directory aren't wrapped by jest.fn() by default.
Personally I find the thing a bit confusing.
So you can do both
function x(data) {
cache.push(data);
return cache;
}
jest.mock('a-a', () => ({x: x}))
if you do everything in the same file, or
jest.mock('a-a');
and then in the __mocks__/a-a.js file
export const x = jest.fn(async (data) => {
cache.push(data);
return cache;
});

How to test javascript function independently with mocha chai and sinon?

I am new to unit testing and have been reading a few tutorials about this practice with javascript. I will use a silly example to explain my problem.
Let's say John needs to go to school and before knowing if he's ready to go he has to check if he has his bag and his headphones. This would be called with the following function:
john.isReadyToGo;
The implementation of the isReadtToGo() function for a character object is as follows:
characher.isReadyToGo = function() {
return this.hasBag() && this.hasHeadPhones();
}
characher.hasBag = function() {
// return true or false
}
characher.hasHeadPhones = function() {
//return true or false
}
Now, let's say I want to test this function. In unit testing it is recommended to test functions without them being affected by other functions. This means that in this case I would have to test the three functions but the character.isReadyToGo() function would need to have mock values for this.hasBag() and this.hasHeadPhones(). Am I right?
If so, could you give me a hint on how I can mock these two values?
Here's an example:
let character = {};
character.isReadyToGo = function() {
return this.hasBag() && this.hasHeadPhones();
}
character.hasBag = function() {
// return true or false
}
character.hasHeadPhones = function() {
//return true or false
}
const sinon = require('sinon');
const expect = require('chai').expect;
describe('Is character ready?', () => {
beforeEach(() => {
sinon.stub(character, 'hasBag');
sinon.stub(character, 'hasHeadPhones');
});
afterEach(() => {
character.hasBag.restore();
character.hasHeadPhones.restore();
});
it("Not if they don't have a bag or headphones", () => {
character.hasBag.returns(false);
character.hasHeadPhones.returns(false);
expect(character.isReadyToGo()).to.be.false;
});
it("Not if they have headphones but no bag", () => {
character.hasBag.returns(false);
character.hasHeadPhones.returns(true);
expect(character.isReadyToGo()).to.be.false;
});
it("Not if they have a bag but no headphones", () => {
character.hasBag.returns(true);
character.hasHeadPhones.returns(false);
expect(character.isReadyToGo()).to.be.false;
});
it("Yes, if they have a bag and headphones", () => {
character.hasBag.returns(true);
character.hasHeadPhones.returns(true);
expect(character.isReadyToGo()).to.be.true;
});
});
For each test, this stubs character.hasBag and character.hadHeadphones (this is done in beforeEach). This basically replaces the original with a new function (the stub) that you can control.
Depending on the test, the stub is "told" what to return for each function (using .returns()), isReadyToGo is called, and its result is checked against the expectation.
After each test, the stub is restored (meaning that the original function is restored).

Resources