Jest SpyOn __mocks__ module - node.js

We mock our modules using __mocks__. I'd like to spyOn a module function in my test but it doesn't seem to be working. Related question, I'd also like to override a mocked module function for a test (ie throw an exception). How can I do this?
├──__mocks__
| └──DB-Utils.js
| └──controllers
| └──myController.js
├──node_modules
__mocks__/DB-Utils.js:
const { MyController } = require('./controllers/myController');
module.exports = {
MyController,
};
__mocks__/controllers/myController.js:
class MyController {
async setAvailability(id, availability) {
return true;
}
}
module.exports = {
MyController,
};
test.spec.js:
const { MyController } = require('DB-Utils');
const myController = new MyController();
describe('Register Tests', () => {
fit('myController setAvailability', async () => {
---code that calls a class that ends up calling myController.setAvailability---
expect(myController.setAvailability).toHaveBeenCalledWith('foo', 'bar');
});
});
My tests pass in that the mock myController is called, however it fails the toHaveBeenCalledWith with an error of Number of calls: 0
How can I spy setAvailability?
For the related question I'd also like to be able to do something like the following:
describe('Register Tests', () => {
fit('myController setAvailability throws', async () => {
jest.spyOn(myController, 'setAvailability').mockImplementation(() => {
throw new Error()
});
expect(---code that calls a class that ends up calling myController.setAvailability---).toThrow();
});
});

Related

can't get the Jest provided ESM example to run

I'm just trying to get the ES6 Class Mocks example provided by Jest to run green.
here's my code repo
it's taken me way to long to even get to this point, but the tests still fail with
TypeError: SoundPlayer.mockClear is not a function
system under test
import SoundPlayer from './sound-player';
export default class SoundPlayerConsumer {
constructor() {
this.soundPlayer = new SoundPlayer();
}
playSomethingCool() {
const coolSoundFileName = 'song.mp3';
this.soundPlayer.playSoundFile(coolSoundFileName);
}
}
the test
import {jest} from '#jest/globals';
import SoundPlayer from './sound-player';
import SoundPlayerConsumer from './sound-player-consumer';
const mockPlaySoundFile = jest.fn();
jest.mock('./sound-player', () => {
return jest.fn().mockImplementation(() => {
return {playSoundFile: mockPlaySoundFile};
});
});
beforeEach(() => {
SoundPlayer.mockClear();
mockPlaySoundFile.mockClear();
});
it('The consumer should be able to call new() on SoundPlayer', () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
// Ensure constructor created the object:
expect(soundPlayerConsumer).toBeTruthy();
});
it('We can check if the consumer called the class constructor', () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
expect(SoundPlayer).toHaveBeenCalledTimes(1);
});
it('We can check if the consumer called a method on the class instance', () => {
const soundPlayerConsumer = new SoundPlayerConsumer();
const coolSoundFileName = 'song.mp3';
soundPlayerConsumer.playSomethingCool();
expect(mockPlaySoundFile.mock.calls[0][0]).toEqual(coolSoundFileName);
});
system under test dependency
export default class SoundPlayer {
constructor() {
this.foo = 'bar';
}
playSoundFile(fileName) {
console.log('Playing sound file ' + fileName);
}
}

Jest - mock function inside another module function

I'm trying to mock the return value (or implementation) of the functions inside another module's function with Jest. I need to test different scenarios (function throws error, returns null, returns an object, etc...)
That module (userService) returns a function that returns an object with that functions:
userService.js (I want to mock the return value of findUser & createUser)
...
function userService(userModel) {
async function findUser(userQuery) {
...
return foundUser;
}
async function createUser(user) {
...
return createdUser;
}
return { findUser, createUser };
}
module.exports = userService;
And I'm testing authStravaController, which uses that service functions:
authStravaController
...
const authStravaServiceRaw = require('../../services/authStravaService');
const userServiceRaw = require('../../services/userService');
const bikeServiceRaw = require('../../services/bikeService');
...
function authStravaController(userModel, bikeModel) {
const { findUser, createUser } = userServiceRaw(userModel); <-- WANT TO MOCK THAT FUNCTIONS
async function authStrava({ body: { authCode } }, res) {
...
try {
...
const findUserQuery = {
stravaUserId: stravaBasicUser.stravaUserId,
};
authUser = await findUser(findUserQuery); <-- MOCK THIS FUNCTION RETURN MULTIPLE TIMES
if (!authUser) {
resStatus = CREATED;
createdUser = await createUser(stravaBasicUser); <-- SAME
...
createdUser.bikes = createdBikes.map((bike) => bike._id);
createdUser.save();
authUser = { createdUser, createdBikes };
}
return handleResponseSuccess(res, resStatus, authUser);
} catch (authStravaError) {
return handleResponseError(res, authStravaError);
}
}
return { authStrava };
}
module.exports = authStravaController;
At the moment I've been able to mock the function return value just 1 time, and I can't find a way to rewrite it, so now I can only test 1 scenario
This code at the top of the file let me test 1 scenario
jest.mock('../../services/userService', () => () => ({
findUser: jest.fn().mockReturnValue(1),
createUser: jest.fn().mockReturnValue({ username: 'userName', save: jest.fn() }),
}));
I've tried to mock it in multiple ways and can't get it to work, how could I do it to test different return values:
findUser: jest.fn().mockReturnValue(1),
findUser: jest.fn().mockReturnValue(undefined),
findUser: jest.fn().mockReturnValue({user:'username'}),
etc...
Thanks!
I fixed it importing all the services outside the controller function, at the top of the file.
This way I can mock the returnValue of any function.

Sinon Stub depedent class in node.js module

I have one class as below
nx-user.js
class NXUser {
constructor() {}
view(guid, data) {
//do something
}
}
Then I have user controller module as below which has dependency of NxUser class
userController.js
const userDb = new NXUser();
import NXUser from "../../../persistence/nx-user";
const allUsers = () => {
return userDb.view()
}
export {allUsers }
I have below code written for stubbing view function of NxUser class for controller unit tests. But its not working. It always calling actual one instated of stubbed one
userController-test.js
let userdb=NXUser();
describe("user controller", function () {
let stubValue = [{
"name": "Urvashi Parmar",
"email": "urvashi.parmar#nationalexpress.com"]}
it("Should create user", () => {
sinon.stub(userdb, 'create').resolves(stubValue);
userController.allUsers ().then((body) => {
expect(body[0].name).to.equal(stubValue .name);
done();
});
})
}
Because I can not comment yet, so I need to give full answer.
Confusion: at your userController-test.js, you are trying to test NXUser.create, while at file nx-user.js has no definition of create.
Assume: you are trying to test NXUser.view.
This example is created based on your code, and is working fine. Console log "Called" will not get called.
Highlight:
Stub NXUser view directly, not userdb.create;
I use async-await inside test.
const sinon = require('sinon');
const { expect } = require('chai');
class NXUser {
constructor() {}
view(guid, data) {
console.log('Called');
return new Promise((resolve) => resolve([]));
}
// Add this only for dummy.
create() {
return new Promise((resolve) => resolve([]));
}
}
const userController = {
allUsers() {
const userDb = new NXUser();
return userDb.view();
}
}
describe('user controller', function () {
// Suppose you test view user.
it('should view user', async function () {
const stubValue = [{
name: 'Urvashi Parmar',
email: 'urvashi.parmar#nationalexpress.com'
}];
// Suppose you stub method view and not create.
const stubUserDBView = sinon.stub(NXUser.prototype, 'view');
stubUserDBView.resolves(stubValue);
const body = await userController.allUsers();
expect(body).to.be.an('array').that.have.lengthOf(1);
expect(body[0]).to.have.property('name', stubValue[0].name);
// Restore stub.
stubUserDBView.restore();
});
});
$ npx mocha stackoverflow.js
user controller
✓ should view user
1 passing (11ms)
$
Hope this helps.

How to test mongoose in NestJS Service?

I would like to test getFund() method from my service. I use NestJS that uses jest by default.
I have no idea how to test this line with jest: return await this.fundModel.findById(id);. Any idea?
import { Injectable } from '#nestjs/common';
import { Model } from 'mongoose';
import { Fund } from '../../funds/interfaces/fund.interface';
import { InjectModel } from '#nestjs/mongoose';
#Injectable()
export class FundService {
constructor(
#InjectModel('Fund')
private readonly fundModel: Model<Fund>,
) {}
/*****
SOME MORE CODE
****/
async getFund(id: string): Promise<Fund> {
return await this.fundModel.findById(id);
}
}
Edit
Thanks to slideshowp2 answer, I wrote this test.
describe('#getFund', () => {
it('should return a Promise of Fund', async () => {
let spy = jest.spyOn(service, 'getFund').mockImplementation(async () => {
return await Promise.resolve(FundMock as Fund);
});
service.getFund('');
expect(service.getFund).toHaveBeenCalled();
expect(await service.getFund('')).toEqual(FundMock);
spy.mockRestore();
});
});
The problem is that I get this result in my coverage report:
When I hover the line I get statement not covered.
There is only one statement return await this.fundModel.findById(id); in your getFund method. There is no other code logic which means the unit test you can do is only mock this.fundModel.findById(id) method and test
it .toBeCalledWith(someId).
We should mock each method and test the code logic in your getFund method. For now, there is no other code logic.
For example
async getFund(id: string): Promise<Fund> {
// we should mock this, because we should make an isolate environment for testing `getFund`
const fundModel = await this.fundModel.findById(id);
// Below branch we should test based on your mock value: fundModel
if(fundModel) {
return true
}
return false
}
Update
For example:
describe('#findById', () => {
it('should find ad subscription by id correctly', async () => {
(mockOpts.adSubscriptionDataSource.findById as jestMock).mockResolvedValueOnce({ adSubscriptionId: 1 });
const actualValue = await adSubscriptionService.findById(1);
expect(actualValue).toEqual({ adSubscriptionId: 1 });
expect(mockOpts.adSubscriptionDataSource.findById).toBeCalledWith(1);
});
});
The test coverage report:

How to test setInterval on a method?

I have a class
class Dummy {
constructor() {
this.prop1 = null;
this.prop2 = null;
this.prop3 = setInterval(() => {
this.method1()
}, 1000);
}
method1() {
// Method logic
}
}
var dummyObject = new Dummy();
module.exports = dummyObject;
I'd like to write tests to verify that method1 is being invoked after every 1s.
Following is the test code
const dummyObject = require('./dummy.js');
describe("Test setInterval", function () {
it("Test setInterval", function () {
const clock = sinon.useFakeTimers();
const spy = sinon.spy(dummyObject, 'method1');
clock.tick(1001);
expect(spy.calledOnce).to.be.true;
clock.restore();
})
})
When I run the tests however, I get an error 'Expected false to equal to true' and on further digging I realized I am not able to spy on the method which is being called via setInterval.
Please share any thoughts on what I can do to test this scenario?
This is not going to work the way you want it to, because the method (method1) is already called when you require the module and hence there is no chance to spy it afterwards in your test.
I recommend to refactor your Module to export the class, not the instance like:
module.exports = class Dummy {
constructor() {
this.prop1 = null;
this.prop2 = null;
this.prop3 = setInterval(() => {
this.method1()
}, 1000);
}
method1() {
// Method logic
}
}
Then in you test, require the class and spy on the method before you instantiate it:
const sinon = require('sinon');
const Dummy = require('./dummy.js');
describe("Test setInterval", function () {
it("Test setInterval", function () {
const clock = sinon.useFakeTimers();
// Spy on the method using the class' prototype
const spy = sinon.spy(Dummy.prototype, 'method1');
// Initialize the class and make sure its `constructor` is called after you spied on the method
new Dummy();
clock.tick(1001);
expect(spy.calledOnce).to.be.true;
clock.restore();
})
})

Resources