How can i unit test the dependency method - node.js

It's my code written in Typescript;
I want to test the private getFunc method and the method of redisClient have been called Once.
I use supertest to invoke the API, but I can't expect the redis method.
import { Request, Response, Router } from "express";
import * as redis from "redis";
const redisOption: redis.ClientOpts = {
host: "127.0.0.1",
port: 6379,
detect_buffers : true,
db: 0,
retry_strategy: () => 60000
}
const redisClient: redis.RedisClient = redis.createClient(redisOption);
export class IndexRoutes {
public router: Router;
constructor() {
this.router = Router();
this.init();
}
public init() {
this.router.get("/", this.getFunc);
}
private getFunc = async (req: Request, res: Response) => {
return res.status(200).send(await redisClient.set("test", "123"));
}
}
error: Uncaught AssertionError: expected get to have been called
exactly once, but it was called 0 times
Help me, how do I properly stub the redisClient.get(...) function?

First of all, you don't usually test dependencies/dependency methods. You only test your code.
Secondly, I think you're saying you want want to check if redis.get() is being called or not. That means you'll have to spy on it.
jest.spyOn() is something you should check out.
Your test should look something like:
import * as redis from 'redis';
describe('my redis wrapper', () => {
it('Should call get when my wrapper\'s getFunc is called', () => {
let myRedisSpy = jest.spyOn(redis.prototype, 'get');
// call your function here
expect(myRedisSpy).toHaveBeenCalledOnce();
});
});
Or something similar, I don't know if this code will work as is. But, you're always welcome to try.

Related

Dependecy Injection using Class into Express

I'm using Express into a TypeScript project and I have the following situation
This is my route file
...
import findAllUsersFactory from "src/factory/FindAllUsers";
routes.get("/users", findAllUsersFactory().handle);
...
This is the factory where I do a sequence of injections
const findAllUsersFactory = () => {
const findAllUserRepository = new PrismaUsersRepository();
const findAllUsersBusiness = new FindAllUsersBusiness(findAllUserRepository);
const findAllUsersController = new FindAllUsersController(findAllUsersBusiness);
return findAllUsersController;
};
This is my Controller
class FindAllUsersController {
constructor(private findUserBusiness: FindAllUsersBusiness) { }
async handle(request: Request, response: Response) {
const allUsers = await this.findUserBusiness.execute();
return response.status(200).send({ allUsers });
}
}
And finally my Business
class FindAllUsersBusiness {
constructor(private usersRepository: IUsersRepository) {}
async execute() {
return this.usersRepository.findAll();
}
}
The problem is that I'm getting an error "Cannot read property 'execute' of undefined" because the findUserBusiness into handle function is undefined. And what I can't understand is that if I change my route to
routes.get("/users", (request, response) => {
findAllUsersFactory().handle(request, response);
});
it works
I've tried to log the functions, but I can say why findUserBusiness is undefined since it came from the constructor, and since the handle functions came from an instance of FindAllUsersController it should have it "defined"
You need to make some adjustments in order to adapt your factory to the way router.get expects its parameters.
const findAllUsersFactory = (req, res) => {
const findAllUserRepository = new PrismaUsersRepository();
const findAllUsersBusiness = new FindAllUsersBusiness(findAllUserRepository);
const findAllUsersController = new FindAllUsersController(findAllUsersBusiness);
return findAllUsersController.handle(req, res)
};
Then in your router you need to do the following:
routes.get("/users", findAllUsersFactory);

Mocha, Supertest and Mongo Memory server, cannot setup hooks properly

I have a Nodejs, Express server that uses Mongodb as the Database. I am trying to write some tests but I cannot get it configured properly. I need to create a mongoose connection for each block ( .test.ts file) once and then clean up the db for the other tests. Depending on how I approach this I get two different behaviours. But first my setup.
user.controller.test.ts
import { suite, test, expect } from "../utils/index";
import expressLoader from "#/loaders/express";
import { Logger } from "winston";
import express from "express";
import request from "supertest";
import { UserAccountModel } from "#/models/UserAccount";
import setup from "../setup";
setup();
import { app } from "#/server";
let loggerMock: Logger;
describe("POST /api/user/account/", function () {
it("it should have status code 200 and create an user account", async function () {
//GIVEN
const userCreateRequest = {
email: "test#gmail.com",
userId: "testUserId",
};
//WHEN
await request(app)
.post("/api/user/account/")
.send(userCreateRequest)
.expect(200);
//SHOULD
const cnt: number = await UserAccountModel.count();
expect(cnt).to.equal(1);
});
});
And my other test post.repository.test.ts
import { suite, test, expect } from "../../utils/index";
import { PostModel } from "#/models/Posts/Post";
import PostRepository from "#/repositories/posts.repository";
import { PostType } from "#/interfaces/Posts";
import { Logger } from "winston";
#suite
class PostRepositoryTests {
private loggerMock: Logger;
private SUT: PostRepository = new PostRepository({});
#test async "Should create two posts"() {
//GIVEN
const given = {
id: "jobId123",
};
//WHEN
await this.SUT.CreatePost(given);
//SHOULD
const cnt: number = await PostModel.count();
expect(cnt).to.equal(1);
}
}
And my setup
setup.ts
import { MongoMemoryServer } from "mongodb-memory-server";
import mongoose from "mongoose";
export = () => {
let mongoServer: MongoMemoryServer;
before(function () {
console.log("Before");
return MongoMemoryServer.create().then(function (mServer) {
mongoServer = mServer;
const mongoUri = mongoServer.getUri();
return mongoose.connect(mongoUri);
});
});
after(function () {
console.log("After");
return mongoose.disconnect().then(function () {
return mongoServer.stop(true);
});
});
};
With the above setup I get
1) "before all" hook in "{root}":
MongooseError: Can't call `openUri()` on an active connection with different connection strings. Make sure you aren't calling `mongoose.connect()` multiple times. See: https://mongoosejs.com/docs/connections.html#multiple_connections
But if I don't import the setup.ts and I rename it to setup.test.ts, then it works but the data isn't cleared after each run so I actually have 10 new users created instead of 1. Every time I run it, it works and doesn't clear the data after it's finished.
Also I have a big issue where the tests hang and don't seem to finish. I am guessing that is because of the async, await in the tests or because of the hooks hanging.
What I want to happen is:
Each test should have it's own setup, the mongo memory server should be clean every time.
The tests should use async and await and not hang.
Somehow export the setup from 1) as a utility function so that I can reuse it in my code

Create IncomingMessage test dummy in Jest

I am trying to write a unit test for a function that takes IncomingMessage as a parameter. I understand it is a stream but I am unsure how to create a basic test dummy as the stream causes my tests to timeout
: Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Error:
My class
import { IncomingMessage } from 'http';
export class MyClass
example(request: IncomingMessage): void {
console.log(request.get('hello?'))
}
}
My test
it('test', () => {
const myclass = new MyClass();
const request = {
get: () => 'hello!'
} as unknown as IncomingMessage
const response = myclass.example(request);
expect(response).toEqual('hello!');
});
I assume I need to close the stream so how can I do that when casting a 'dummy' object to type IncomingMessage?
I tried using
const request = {
get: () => 'hello'
} as unknown as IncomingMessage
request.setTimeout(1)
but then I get an error that the function is not found, which makes sense since the dummy is basically an empty object with only one mocked function
TypeError: request.setTimeout is not a function
In this case I assume I need to actually create a real IncomingMessage? But I cannot find much documentation or examples on how to do this
What is the best way to create a real IncomingMessage stream, close it quickly and have a mocked get method? I think I would then need to mock a socket?
Or what is the correct way to test my class and function in this case?
Basically you need to mock your request object that can be done with below snippet:
function _mock_req() {
let req = {
get: jest.fn(() => 'hello!'),
};
return req;
}
Once mocking is done invoke it in your test case, here I'm using beforeEach that will take care of the issue: openHandler- Async callback was not invoked.
describe('http test', () => {
let req, res;
const myclass = new MyClass();
beforeEach((done) => {
req = _mock_req();
res = myclass.example(req);
done();
});
it('class test', () => {
expect(res).toEqual('hello!');
});
});
You also need to return request from your class to test get passed. While executing test case your mocked request object will get return from MyClass
export class MyClass {
example(request: IncomingMessage): void {
console.log(request.get('hello?'));
return request.get('hello?')
}
}
Test case result:
If you're not using express and using the built in http library for node you can do the following:
Note: This will also work with express too
import httpMocks from 'node-mocks-http';
import handleRequest from '../../../handlers/request';
describe('incoming message mock', () => {
it('working example', () => {
const mockRequest = httpMocks.createRequest({
method: 'POST',
url: '/my-fantastic-endpoint',
headers: {
'content-type': 'application/json',
'accept': 'application/json',
'content-length': '1',
'x-forwarded-for': '127.0.0.1',
},
});
mockRequest.destroy = jest.fn();
const mockResponse = httpMocks.createResponse();
handleRequest(mockRequest, mockResponse);
mockRequest.send({
example: 'hello!',
});
console.log(mockResponse._getData());
expect(true).toBe(true);
});
});

How to inject dependencies into Firebase/Google Cloud Functions? (unit & integration testing)

I don't know whether my question is really related to Firebase Cloud Functions, but I came across this problem trying to test my Firebase Cloud Functions.
Let's say I have a Firebase Cloud function written in NodeJS:
function.ts
import * as functions from "firebase-functions"
const admin = require("firebase-admin")
import * as authVerifier from "../../auth/authVerifier"
export default functions.https.onRequest(async (req, res) => {
let authId
try {
authId = await authVerifier.identifyClientRequest(req)
} catch (err) {
console.error(`Unauthorized request error: ${err}`)
return res.status(401).send({
error: "Unauthorized request"
})
}
}
Usually I have an interface and can easily mock any class I want to test it.
And, for example, authVerifier looks like:
authVerifier.ts
import * as express from "express"
export async function identifyClientRequest(req: express.Request) {
return true // whatever, it doesn't really matter, should be real logic here
}
I'm trying to test function.ts and I only can pass res and req into it, e.g:
function.test.ts
it("should verify client identity", async () => {
const req = {
method: "PUT"
}
const res = { }
await myFunctions(req as express.Request, res as express.Response)
// assert that authVerifier.identifyClientRequest(req) called with passed req
})
So the question is: how can I mock authVerifier.identifyClientRequest(req) to use different implementations in function.ts and in function.test.ts?
I don't really know NodeJS/TypeScript, so I wonder if I can import another mock class of authVerifier for test or something like that.
Ok, seems like I found the answer. I'll post it here just in case.
Using sinonjs, chai we can stub our class (authVerifier in that case) to return necessary results:
const chai = require("chai")
const assert = chai.assert
const sinon = require("sinon")
import * as authVerifier from "../../../src/auth/authVerifier"
it("should verify client identity", async () => {
const req = {
method: "PUT"
}
const res = mocks.createMockResponse()
const identifyClientRequestStub = sinon.stub(authVerifier, "identifyClientRequest");
const authVerifierStub = identifyClientRequestStub.resolves("UID")
await updateUser(req as express.Request, res as express.Response)
assert.isTrue(authVerifierStub.calledWith(req))
})
And the result is:

Is there a trick to using Mockery in Mocha test with Typescript?

It would seem the usual method of importing in typescript prevents the modules from being mocked... Assume I have the following product code in a node.js project written in typescript that I would like to test:
// host.ts
import http = require('http');
export class Host {
public start(port: number): http.Server {
return http.createServer().listen(port);
}
}
I have the below unit test using mockery (d.ts in pull request #3313) and mocha:
import chai = require('chai');
import mockery = require('mockery');
import webserver = require('../hosting/host');
describe('host', (): void => {
describe('start()', (): void => {
before(() : void => {
mockery.enable();
});
after((): void => {
mockery.deregisterAll();
mockery.disable();
});
it('should create an http server', (): void => {
mockery.registerMock('http', {
Server: mocks.Server,
createServer: (app: any) : any => new mocks.Server(app)
});
var host: webserver.Host = new webserver.Host({ port: 111 });
var server: any = host.start();
chai.expect(server).is.instanceOf(mocks.Server);
});
});
});
module mocks {
'use strict';
export class Server {
app: any;
constructor(app: any) {
this.app = app;
}
}
}
The problem is that when import webserver = require('../hosting/host') is called the mocks in the test aren't setup yet and the un-mocked require('http') is returned. I attempted to try var http = require('http') within the Host.start function, but this prevents http.Server from being declared as a return value.
How should I go about implementing unit tests in Typescript with Mocks? Is there a better library than mockery that would work better?
After all day of scouring the web I finally learned that: Yes, there is a trick to using Mockery in Mocha test with Typescript. The trick is using the typeof identifier to reference the module. I discovered this in the Optional Module Loading and Other Advanced Loading Scenarios in this document.
My updated code now looks like this:
// host.ts
import httpDef = require('http');
export class Host {
public start(port: number): httpDef .Server {
var http: typeof httpDef = require('http');
return http.createServer().listen(port);
}
}
This allows me to set up mocks in my mocha test like this:
import chai = require('chai');
import mockery = require('mockery');
import webserver = require('../hosting/host');
import httpDef = require('http'):
describe('host', (): void => {
describe('start()', (): void => {
before(() : void => {
mockery.enable();
});
after((): void => {
mockery.deregisterAll();
mockery.disable();
});
it('should create an http server', (): void => {
var mockServer: httpDef.Server = <httpDef.Server>{};
var mockHttp: typeof httpDef = <typeof httpDef>{};
mockHttp.createServer = () : httpDef.Server => mockServer;
mockery.registerMock('http', mockHttp);
var host: webserver.Host = new webserver.Host({ port: 111 });
var server: any = host.start();
chai.expect(server).is.equals(mockServer);
});
});
});
Some other scenarios where this can be used for dependency injection can be found here.

Resources