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:
Related
I'm looking to see if there is a recommended way to write a JEST test that an Interceptor has been called. In the example below LoggingInterceptor was called? The purpose of test is verify that NestJS Binding interceptors is in place.
import { Controller, Get, UseInterceptors } from '#nestjs/common';
import { AppService } from './app.service';
import { LoggingInterceptor, TransformInterceptor } from './transform.interceptor';
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#UseInterceptors(LoggingInterceptor)
#Get()
getHello(): string {
return this.appService.getHello();
}
}```
I would advise against testing that. Think about what you're testing: you're testing that the framework is doing what it says it will, something that the framework itself already tests. Sure you could use the Reflect API and verify that that metadata does exist, but that's something the framework should assert, it's a feature you should just be able to use with peace of mind.
One option I used to create a Jest test to verify that a binding interceptor(Which transformed the response) on a controller was called and produced the expected response was by using NestJS Supertest lib to simulate an end to end test.
Related NestJS doc:
List item
https://docs.nestjs.com/fundamentals/testing#end-to-end-testing
Test Code Sample:
import { INestApplication } from '#nestjs/common';
import { Test } from '#nestjs/testing';
import * as request from 'supertest';
import { CatsModule } from '../../src/cats/cats.module';
import { CatsService } from '../../src/cats/cats.service';
import { CoreModule } from '../../src/core/core.module';
describe('Test interceptor response binding was triggerred', () => {
const aServiceResponse = { findAll: () => ['test'] };
const expectedResponseAfterTransformation = { code: 200, data: [ 'test' ] };
let app: INestApplication;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [CatsModule, CoreModule],
})
.overrideProvider(CatsService)
.useValue(aServiceResponse)
.compile();
app = moduleRef.createNestApplication();
await app.init();
});
it(`/GET cats returns transformed data respsone`, () => {
return request(app.getHttpServer()).get('/cats').expect(200).expect({
data: expectedResponseAfterTransformation,
});
});
afterAll(async () => {
await app.close();
});
});
I'm trying to test my controller function which is:
import { InstalledPackages } from '../parser/parser.service';
import {
getOutdatedPackages,
InstalledPackageStatus,
} from './version-control.service';
interface OutdatedPackages {
dependencies: InstalledPackageStatus[];
devDependencies: InstalledPackageStatus[];
}
export async function getPackagesUpdatesToNotify(
packages: InstalledPackages,
type = 'package.json',
): Promise<OutdatedPackages> {
return getOutdatedPackages(packages.dependencies, type);
}
And having my service like this:
import { fetch } from "../common/http.service";
export async function getLastPackageVersion(
packageName: string
): Promise<VersionType> {
const url = `https://registry.npmjs.org/-/package/${packageName}/dist-tags`;
return await (<Promise<VersionType>>fetch(url));
}
export async function getOutdatedPackages(
installedPackages: PackagesVersion,
type: string
): Promise<InstalledPackageStatus[]> {
return Promise.all(
Object.keys(installedPackages).map(async (packageName) =>
getLastPackageVersion(packageName)
)
);
}
I've already tried both solutions:
import * as myService from './my.service';
it('my test', async () => {
const getLastPackageVersionSpy = jest.spyOn(myService, 'getLastPackageVersion').mockReturnValue(
Promise.resolve(42),
await getPackagesUpdatesToNotify(packages, type)
});
and
import { getLastPackageVersion } from './my.service';
import { getPackagesUpdatesToNotify } from './version-control.controller';
jest.mock('./myse.service', () => ({
...jest.requireActual('./myse.service'),
getLastPackageVersion: jest.fn(),
}));
it('my test', async () => {
(getLastPackageVersion as jest.Mock).mockResolvedValue(
Promise.resolve(42),
);
await getPackagesUpdatesToNotify(packages, type)
});
But the original function is always called instead of the mocked one.
How to mock the getLastPackageVersion method.
I'm trying to avoid using tools like rewire if possible.
Thank you
Move the getLastPackageVersion to different file, import it in the my.service and then mock it.
my.service:
import { fetch } from "../common/http.service";
import { getLastPackageVersion } from '../last-package-version';
export async function getOutdatedPackages(
installedPackages: PackagesVersion,
type: string
): Promise<InstalledPackageStatus[]> {
return Promise.all(
Object.keys(installedPackages).map(async (packageName) =>
getLastPackageVersion(packageName)
)
);
}
import * as lastPackageVersion from '../last-package-version';
it('my test', async () => {
const getLastPackageVersionSpy = jest.spyOn(lastPackageVersion, 'getLastPackageVersion').mockResolvedValue(42);
await getPackagesUpdatesToNotify(packages, type)
});
getOutdatedPackages is in the same file as the getLastPackageVersion so it cannot be mocked. In your case the getOutdatedPackages is still using the original getLastPackageVersion method.
My e2e test is returning TypeError: metadata_1.Public is not a function for a controller that is using the custom decorator #Public()
Some code is omitted for clarity
it(`/GET forks`, async () => {
const fork: ForksModel = {
type: 'Full Copy',
};
await request(app.getHttpServer())
.get('/forks')
.expect(200)
.expect({ fork: expectedForks});
});
#Public()
public async getAccountForks(#Req() req: Request) {
const { account } = req;
const fork = await this.service.getAccountForks(account);
return { fork, account };
}
public.decorator.ts
import { SetMetadata } from "#nestjs/common";
export const Public = () => SetMetadata( "isPublic", true );
I don't know what is happening here, it doesn't complain this when running nest
This is imported
import { Public } from '#app/utils/metadata';
So i just forgot to export my metadata files from the root utils index.ts!
But Nest didn't complain and the decorator was functional on my Guard when testing!
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.
I'm new to NodeJS and came from PHP, where creating partial mock was easy. But I'm not able to accomplish the same with Jest in NodeJS.
I have a function extractPayloadDates which accept an instance of dialogflow Agent and taking and parsing data from it. I want to mock only single method getParameter of Agent, because no more methods are used in the tested function. I found this code online but it doesn't work
import { Agent } from '../../src/dialogflow/Agent';
import { extractPayloadDates } from '../../src/intents/extractPayloadDates';
describe('extractPayloadDates', () => {
it('tests extracting string', () => {
const AgentMock = jest.fn<Agent, []>(() => ({
getParameter: () => {
return 'some date';
}
}));
const agent = new AgentMock();
expect(extractPayloadDates(agent)).toEqual('some date');
});
});
This code produce following error:
Type '{ getParameter: () => string; }' is missing the following properties from type 'Agent': payload, webhookClient, chatter, getOriginalRequest, and 13 more.ts(2740)
index.d.ts(124, 53): The expected type comes from the return type of this signature.
I also tried to use jest.spyOn, but the problem is, that I cannot create Agent instance as it needs many other objects.
Edit 3.9.2019 with more code
Agent.ts
export class Agent {
private payload: DialogFlowPayload[] = [];
constructor(readonly webhookClient: WebhookClient, private readonly chatter: Chatter) {}
...
}
WebhookClient and Chatter have more dependencies in their constructor as well...
extractPayloads.spec.ts
import { Agent } from '../../src/dialogflow/Agent';
import { extractPayloadDates } from '../../src/intents/extractPayloadDates';
describe('extractPayloadDates', () => {
it('tests extracting string', () => {
const webhookMock = jest.fn();
const chatter = jest.fn();
const agent = new Agent(webhookMock, chatter);
expect(extractPayloadDates(agent)).toEqual('some date');
});
});
This produce another error:
Argument of type 'Mock' is not assignable to parameter of type 'Chatter'.
Property 'getMessage' is missing in type 'Mock' but required in type 'Chatter'
Do I really have to create also WebhookClient and all its dependencies and do the same with Chatter? If I do, I have to create instances of multiple classes just to mock 1 method in Agent, which then will not use any of dependency.
From the docs of jest
If you want to overwrite the original function, you can use jest.spyOn(object, methodName).mockImplementation(() => customImplementation) or object[methodName] = jest.fn(() => customImplementation);
jest.spyOn call jest.fn internally. So you can only mock getParameter method of agent like this:
extractPayloadDates.ts:
function extractPayloadDates(agent) {
return agent.getParameter();
}
export { extractPayloadDates };
Agent.ts:
interface DialogFlowPayload {}
interface WebhookClient {}
interface Chatter {
getMessage(): any;
}
class Agent {
private payload: DialogFlowPayload[] = [];
constructor(readonly webhookClient: WebhookClient, private readonly chatter: Chatter) {}
public getParameter() {
return 'real data';
}
public otherMethod() {
return 'other real data';
}
}
export { Agent, Chatter };
Unit test, only mock getParameter method of agent, keep the original implementation of otherMethod
import { extractPayloadDates } from './extractPayloadDates';
import { Agent, Chatter } from './Agent';
const webhookMock = jest.fn();
const chatter: jest.Mocked<Chatter> = {
getMessage: jest.fn()
};
const agent = new Agent(webhookMock, chatter);
describe('extractPayloadDates', () => {
it('should only mock getParameter method of agent', () => {
agent.getParameter = jest.fn().mockReturnValueOnce('mocked data');
const actualValue = extractPayloadDates(agent);
expect(actualValue).toEqual('mocked data');
expect(agent.otherMethod()).toBe('other real data');
});
});
PASS src/stackoverflow/57428542/extractPayloadDates.spec.ts
extractPayloadDates
✓ should only mock getParameter method of agent (7ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.484s, estimated 3s