I am using mocha and sinon for test the node services, In controller I have getToken npm module for getting the token with name and value as parameters and in spec file I trying to send empty name as parameter using withargs but the response getting success excepted result is token creating fail please help on this issue.
controller.ts
import {getToken} from './tokenUtil';
export async function userInfo(req:Request,res:Response){
try{
let token = await getToken(name,value);
}
catch(error){
res.send({status:'Failue',message:error});
return
}
res.send({status:'success',message:'token creation success'})
}
tokenUtil.ts
export async function getToken(name,value){
// token code here
}
token.spce.ts
import * as sinon from 'sinon';
import * as proxyquire from 'proxyquire';
describe('get token',()=>{
let req:any;
let res:any;
beforeEach(()=>{
res={
send:sinon.stub();
}
it('getting error when given empty name',async()=>{
let tokenStub = sinon.stub().withArgs('',tokenValue).returns(undefined);
let tokenctl=proxyquire('./controller',{
'./tokenUtil':tokenStub
})
await tokenctl.userInfo(req,res);
sinon.assert.calledWithExactly(res.send,{status:'Failue',message:'token creating fail'})
})
})
})
You are testing the controller.ts module, so the test file name should be controller.spec.ts or controller.test.ts.
Since the ./tokenUtil use named exports, so the tokenStub should be an object.
You should use sinon.stub().rejects() to create a promise stub with rejected value.
E.g.
controller.ts:
import { getToken } from './tokenUtil';
import { Request, Response } from 'express';
export async function userInfo(req: Request, res: Response) {
const { name, value } = req.body;
try {
let token = await getToken(name, value);
res.send({ status: 'success', message: 'token creation success' });
} catch (error) {
res.send({ status: 'Failue', message: error });
}
}
tokenUtil.ts:
export async function getToken(name, value) {
// token code here
}
controller.test.ts:
import sinon from 'sinon';
import proxyquire from 'proxyquire';
describe('get token', () => {
let req: any;
let res: any;
beforeEach(() => {
res = {
send: sinon.stub(),
};
});
it('should create token success', async () => {
req = { body: { value: '123', name: 'teresa teng' } };
let tokenStub = {
getToken: sinon.stub().withArgs(req.body.name, req.body.value).resolves(),
};
let tokenctl = proxyquire('./controller', {
'./tokenUtil': tokenStub,
});
await tokenctl.userInfo(req, res);
sinon.assert.calledWithExactly(res.send, { status: 'success', message: 'token creation success' });
});
it('should handle error when given empty name', async () => {
const tokenValue = '123';
req = { body: { value: tokenValue, name: '' } };
const error = new Error('token creating fail');
let tokenStub = {
getToken: sinon.stub().withArgs('', tokenValue).rejects(error),
};
let tokenctl = proxyquire('./controller', {
'./tokenUtil': tokenStub,
});
await tokenctl.userInfo(req, res);
sinon.assert.calledWithExactly(res.send, { status: 'Failue', message: error });
});
});
Test result:
get token
✓ should create token success (101ms)
✓ should handle error when given empty name
2 passing (112ms)
Related
Can someone tell me what mistake I am making or tell me how to set the header in axios patch request. when I am running the API through postman, everything is working fine but when I connect it with the front end, an error comes up saying that the JWT is not provided on the backend
here is the frond end code :
import React, { useEffect } from 'react';
import { useParams } from 'react-router';
import axios from 'axios';
const Loader = () => {
const parmas = useParams();
const { id } = parmas;
console.log(id);
useEffect(() => {
const fetchBags = async () => {
try {
const res = await axios.patch('http://localhost:4001/public/verify', {
headers: {
'Content-Type': 'application/json',
Token: id,
},
});
console.log(res);
console.log('CBM', { res });
} catch (error) {
console.log(error);
}
};
fetchBags();
}, []);
return <div>this is loader</div>;
};
export default Loader;
below is my backend code:
export const verifyUser = async (data) => {
const token1 = data.header("Token");
try {
const verified = jwt.verify(token1, getTokenSecret());
console.log(verified)
await userModel.verifyUser(verified);
return {
message: "success",
};
} catch (error) {
console.log(`Auth Service > verifyUser > ${error.toString()}`);
throw error;
}
};
this error is comming:
Error
From docs
axios.patch(url[, data[, config]])
As you can see you pass config in 3rd argument not 2nd.
const res = await axios.patch(
'http://localhost:4001/public/verify',
{}, // data (2nd argument)
{
headers: {
'Content-Type': 'application/json',
Token: id,
},
} // config (3rd argument)
)
I am using the AWS SDK to send emails. I have the following implementation:
import { Injectable } from '#nestjs/common';
import * as AWS from 'aws-sdk';
import { SendEmailDTO } from 'src/Messages/application/DTO/Inputs';
#Injectable()
export class MailSenderSESSDKAdapter implements MailSenderPort {
constructor() {}
async send(params: SendEmailDTO): Promise<void> {
const sendPromise = new AWS.SES(config).sendEmail(params).promise();
sendPromise
.then(function (data) {
console.log('### MailSenderSESSDKAdapter sendPromise data', data);
})
.catch(function (err) {
console.error('### err', err);
});
}
}
This line:
const sendPromise = new AWS.SES(config).sendEmail(params).promise();
returns a promise and when resolved, if successful, you get back the MessageId of the sent email.
I am trying to test it, I tried following approaches:
import { MailSenderSESSDKAdapter } from '../mailSenderSESSDK.adapter';
jest.mock('../mailSenderSESSDK.adapter', () => {
return jest.fn().mockImplementation(() => {
return { sendPromise: jest.fn() };
});
});
describe('mailSenderSESSDK adapter', () => {
let adapter: MailSenderSESSDKAdapter;
let sendPromise: any;
const mockDataResponse = {
ResponseMetadata: {
RequestId: 'ABC123',
},
MessageId: 'abc-123',
};
beforeEach(async () => {
adapter = new MailSenderSESSDKAdapter();
sendPromise = jest.fn().mockResolvedValue(Promise);
});
it.only('sends an email via SES', async () => {
const sendEmailDTO = {
subject: 'foo subject',
body: 'foo body',
from: 'from#mail.com',
to: 'to#mail.com',
};
await adapter.send(sendEmailDTO);
await expect(sendPromise).resolves.toBe(mockDataResponse);
});
});
But I don't know how to mock a method within a class method. I know how to mock the send method, but not the sendPromise promise inside the send method.
Only idea that I have would be to create a method that wraps the creation of the promise, and then mock this method, but seems overkill.
Anyone knows how to do it or best practices?
I have my system that has custom errors like this:
import { ExtendableError } from './extandable.error'
export const customError = {
EMAIL_EXIST: () => new ExtendableError({
message: 'USER WITH SUCH EMAIL ALREADY EXIST',
code: 403
}),
INVALID_EMAIL: () => new ExtendableError({
message: 'INVALID EMAIL',
code: 400
}),
INVALID_PASSWORD: () => new ExtendableError({
message: 'INVALID EMAIL OR PASSWORD',
code: 403
}),
EMAIL_DOES_NOT_EXIST: () => new ExtendableError({
message: 'EMAIL DOES NOT EXIST',
code: 404
}),
TOKEN_DOES_NOT_EXIST: () => new ExtendableError({
message: 'TOKEN DOES NOT EXIST',
code: 404
}),
DIFFERENT_PASSWORDS: () => new ExtendableError({
message: 'PASSWORDS ARE NOT SAME',
code: 400
}),
CURRENT_PASSWORD_ERR0R: () => new ExtendableError({
message: 'CURRENT PASSWORD IS INCORRECT',
code: 400
}),
SAME_PASSWORDS_ERROR: () => new ExtendableError({
message: 'CANNOT CHANGE SET NEW PASSWORD AS OLD PASSWORD',
code: 403
})
}
Here I can write my own error responses
When I request something, and for example emails exists, it throws an error that such email while registration already exists.
It states here:
async createAccount (doc: RegisterDto, verificationLink: string) {
if (!isValidEmail(doc.email)) {
return customError.INVALID_EMAIL()
}
const user = await this.existByEmail(doc.email)
if (!user) {
return customError.EMAIL_EXIST()
}// HERE I RETURNING AN ERROR TO CONTROLLER
doc.password = await bcrypt.hash(doc.password, SALT_ROUNDS)
const created = await this.repository.create(this.registerMapper.toDomain(doc))
await this.emailService.sendVerificationEmail(created, verificationLink)
return created
}
Here is my function in controller:
#Post('/api/register')
async register(#Body() registerDTO: RegisterDto, #Request() request, #Response() response) {
const verificationLink = `${request.protocol}://${request.header.host}/api/verify-account/`
return await this.service.createAccount(registerDTO, verificationLink)
}
In postman, it shows me the body and the response, but the actual result of the status code has 201 code
How can I fix it?
try this (ref: https://docs.nestjs.com/controllers#library-specific-approach)
import { Controller, Get, Res, HttpStatus } from '#nestjs/common';
import { Response } from 'express';
#Controller('route01')
export class MyController {
#Get('testResponse')
async testFuntion(#Res() response: Response) {
if('some condition') {
return response.status(HttpStatus.OK).send(data);
} else {
return response.status(HttpStatus.BAD_REQUEST).send(data);
}
}
}
if you need to custom function to create your own response you can use
service to create too but need to pass response to function too like this
import { HttpStatus } from '#nestjs/common';
import { Response } from 'express';
service1(response: Response, data: any) {
return response.status(HttpStatus.OK).send(data);
}
#Controller('newcc')
testFuntion(#Res() response: Response) {
const myCustomResp = this.service1(response, 'some data')
}
Instead of return customError.INVALID_EMAIL() you should throw an error.
From npm docs:
let ExtendableError = require('extendable-error');
class MyCustomError extends ExtendableError {};
throw new MyCustomError('A custom error ocurred!');
I'm trying to mock a fetch call using thisfetch-mock-jest but it the code still trys to go to the remote address and eventually fail with error message FetchError: request to https://some.domain.io/app-config.yaml failed, reason: getaddrinfo ENOTFOUND some.domain.io].
Here the the test code
import { AppConfig } from '#backstage/config';
import { loadConfig } from './loader';
import mockFs from 'mock-fs';
import fetchMock from 'fetch-mock-jest';
describe('loadConfig', () => {
beforeEach(() => {
fetchMock.mock({
matcher: '*',
response: `app:
title: Example App
sessionKey: 'abc123'
`
});
});
afterEach(() => {
fetchMock.mockReset();
});
it('load config from remote path', async () => {
const configUrl = 'https://some.domain.io/app-config.yaml';
await expect(
loadConfig({
configRoot: '/root',
configTargets: [{ url: configUrl }],
env: 'production',
remote: {
reloadIntervalSeconds: 30,
},
})
).resolves.toEqual([
{
context: configUrl,
data: {
app: {
title: 'Example App',
sessionKey: 'abc123',
},
},
},
]);
expect(fetchMock).toHaveBeenCalledTimes(1);
});
function defer<T>() {
let resolve: (value: T) => void;
const promise = new Promise<T>(_resolve => {
resolve = _resolve;
});
return { promise, resolve: resolve! };
}
});
loadConfig has the fetch code that I'm trying to mock.
export async function loadConfig(
options: LoadConfigOptions,
): Promise<AppConfig[]> {
const loadRemoteConfigFiles = async () => {
const configs: AppConfig[] = [];
const readConfigFromUrl = async (remoteConfigProp: RemoteConfigProp) => {
const response = await fetch(remoteConfigProp.url);
if (!response.ok) {
throw new Error(
`Could not read config file at ${remoteConfigProp.url}`,
);
}
remoteConfigProp.oldETag = remoteConfigProp.newETag ?? undefined;
remoteConfigProp.newETag =
response.headers.get(HTTP_RESPONSE_HEADER_ETAG) ?? undefined;
remoteConfigProp.content = await response.text();
return remoteConfigProp;
};
.......
return configs;
}
let remoteConfigs: AppConfig[] = [];
if (remote) {
try {
remoteConfigs = await loadRemoteConfigFiles();
} catch (error) {
throw new Error(`Failed to read remote configuration file, ${error}`);
}
}
........ do some stuff with config then return
return remoteConfigs;
}
The config is a yaml file, that eventually gets parsed and converted into config object.
Any idea why is it failing to mock the fetch call?
replaced
import fetchMock from 'fetch-mock-jest';
with
const fetchMock = require('fetch-mock').sandbox();
const nodeFetch = require('node-fetch');
nodeFetch.default = fetchMock;
and fetchMock.mockReset(); with fetchMock.restore();
I want to test the function below using the tests below. However, I fail in mocking the userService.login function, which always returns undefined instead of desired value. I have tried these approaches https://jestjs.io/docs/mock-functions#mock-return-values (please see the tests), but nothing seems to work.
FUNCTION
static async login(req: Request, res: Response, next: NextFunction): Promise<void> {
const userDTO = req.body;
try {
const accessToken = await userService.login(userDTO);
// accessToken is undefined here :(
res.setHeader('authorisation', `Bearer ${accessToken}`);
res.send('Logged in');
} catch (e) {
// eslint-disable-next-line callback-return
next(e);
}
}
TESTS:
import UserService from '#services/users.service';
import UserController from '#routers/controller/users.controller';
import { NextFunction, Request, Response } from 'express';
import UserRepository from '#data-access/user.repository';
jest.mock('#services/users.service');
let userService: UserService;
const fakeToken = 's$sdfDgf45d';
beforeAll(() => {
userService = new UserService(UserRepository);
// userService.login = jest.fn().mockResolvedValue('s$sdfDgf45d').mockRejectedValue('error');
});
describe('UserController: ', () => {
const mockReq = {
body: {
login: 'Artyom',
password: 'qwerty123'
}
};
const mockRes = {
setHeader: jest.fn(),
send: jest.fn()
};
const nextFunction = jest.fn();
test('the class constructor should have been called', () => {
expect(UserService).toHaveBeenCalled();
});
test('should send auth token in case of success scenario', async () => {
// #ts-ignore
userService.login.mockResolvedValue('s$sdfDgf45d').mockRejectedValue('error');
// jest.spyOn(userService, 'login').mockResolvedValue('s$sdfDgf45d' as any).mockRejectedValue('error');
await UserController.login(mockReq as Request, mockRes as any as Response, nextFunction as NextFunction);
// that's the test that fails
expect(mockRes.setHeader.mock.calls[0]).toEqual(['authorisation', `Bearer ${fakeToken}`]);
expect(mockRes.send.mock.calls[0][0]).toEqual('Logged in');
});
test('should throw an error', async () => {
return expect((userService.login as jest.Mock)()).rejects.toMatch('error');
});
});
ERROR:
Array [
"authorisation",
- "Bearer s$sdfDgf45d",
+ "Bearer undefined",
CODE:
https://github.com/lion9/node-express