Nodejs mocking a method - node.js

I want to make a stub of the getDbmodel method.
It is defined in basemodel.js which is
import Sequelize from 'sequelize';
import db from '../../config/database';
let instance = null;
class Database {
static getDbmodel = () => {
if (instance != null) {
return instance;
}
instance = new Sequelize(db.default.database, db.default.username, db.default.password, {
host: db.default.host,
dialect: db.default.dialect,
define: {
timestamps: false, // true by default
},
pool: {
max: db.default.maxConnections,
min: db.default.minConnections,
idle: db.default.idleTimeoutMillis,
timezone: '+00:00',
},
});
console.log('inside basemodel');
return instance;
}
}
export { Database as default };
// ========================================================
// src/Libraries/ABTest.js
import ABTestStorage from '../Storage/ABTest';
class ABTestLib {
getABTestById = id => ABTestStorage.getABTestById(id);
getABTests = () => ABTestStorage.getABTests().then()
.catch(err => console.log(err))
save = abtestObj => ABTestStorage.saveABTest(abtestObj).then()
.catch(err => console.log(err))
put = abtestObj => ABTestStorage.updateABTest(abtestObj)
.then()
.catch(err => console.log(err))
delete = id => ABTestStorage.destroyABTestById(id).then()
.catch(err => console.log(err))
}
export default ABTestLib;
abtest.js imports ab test model which imports basemodel.
This is my test file:
import chai from 'chai';
const SequelizeMock = require('sequelize-mock');
var proxyquire = require('proxyquire');
describe('Libraries/ABTest.js', () => {
before(function () {
let instance = null;
class Database {
static getDbmodel = () => {
if (instance != null) {
return instance;
}
instance = new SequelizeMock();
console.log(instance);
return instance;
}
}
});
var stubs = {
'../..src/Libraries/BaseModel': {
getDbmodel: function () {
console.log('3444e4');
if (instance != null) {
console.log('1111');
return instance;
}
instance = new SequelizeMock();
console.log(instance);
return instance;
}
}
};
const ABTests = proxyquire('../../src/Libraries/ABTest', stubs);
console.log(ABTests);
const abtest = new ABTests.default();
it('get(1): should return response for a abtest', () => {
abtest.getABTestById(1).then((res) => {
chai.expect(res).to.be.an('object');
});
});
it('getAll() : should return response and status 200 for all abtests', () => {
abtest.getABTests().then((res) => {
chai.expect(res).to.be.an('object');
});
});
it('save() : should return response and status 200 for a abtest', () => {
abtest.save({ id: '500', name: 'home', code: 'home', id_organization: '1', id_platform: '1', }).then((res) => {
chai.expect(res).to.be.an('object');
});
});
it('put() : should return response and status 200 for a abtest', () => {
abtest.put({ id: '2', }).then((res) => {
chai.expect(res).to.be.an('object');
});
});
it('delete() : should return response and status 200 for a abtest', () => {
abtest.delete({ id: '111', }).then((res) => {
chai.expect(res).to.be.an('object');
});
});
});
Why does it always go into basemodels gedbmodel and not the mocked one?
I want to mock the getdbmodel method.

Try using noCallThru function in proxyquire:
proxyquire.noCallThru().load('../../src/Libraries/ABTest', stubs).default

Related

Jest - resetting value in module and reloading import

I'm writing a test in jest for a module which uses a constant from a different module.
I want to set a different value for it for every test case, but I don't seem to be able to do so.
The test file:
import { Request, Response } from 'express';
const activityConsumer = require('../../src/utils/activity.consumer');
const mockRequest = {
params: {
activityArn: 'activityArn'
}
} as Request;
const mockedJsonFunction = jest.fn();
const mockResponse: any = {
json: jest.fn(),
status: jest.fn().mockReturnValue({ json: mockedJsonFunction }),
} as Response;
let stopConsumerMock;
describe('consumer handler', () => {
beforeAll(() => {
stopConsumerMock = activityConsumer.stopConsumer = jest.fn().mockReturnValue(1);
});
beforeEach(() => {
jest.resetModules();
});
afterEach(() => {
stopConsumerMock.mockClear();
mockResponse.json.mockClear();
});
describe('stopConsumingHandler', () => {
it('Should return success true and not call stopConsumer when no consumer exists', () => {
activityConsumer.consumer = undefined;
const { stopConsumingHandler } = require ('../../src/handlers/consumer.handlers');
stopConsumingHandler(mockRequest, mockResponse);
expect(stopConsumerMock.mock.calls.length).toEqual(0);
expect(mockResponse.json.mock.calls.length).toEqual(1);
expect(mockResponse.json).toHaveBeenCalledWith({ success: true });
});
it('Should return success true and call stopConsumer when consumer exists', () => {
activityConsumer.consumer = true;
const { stopConsumingHandler } = require ('../../src/handlers/consumer.handlers');
stopConsumingHandler(mockRequest, mockResponse);
expect(stopConsumerMock.mock.calls.length).toEqual(1);
expect(mockResponse.json.mock.calls.length).toEqual(1);
expect(mockResponse.json).toHaveBeenCalledWith({ success: true });
});
});
});
I want to replace the value of activityConsumer.consumer and then reload the consumer.handlers module but the re-assignment and reload does not seem to have any effect.
Please advise on how can I write this test properly.
Try this way, using jest.mock to modify import value of activityConsumer
import { Request, Response } from 'express';
// const activityConsumer = require('../../src/utils/activity.consumer');
const mockRequest = {
params: {
activityArn: 'activityArn'
}
} as Request;
const mockedJsonFunction = jest.fn();
const mockResponse: any = {
json: jest.fn(),
status: jest.fn().mockReturnValue({ json: mockedJsonFunction }),
} as Response;
let stopConsumerMock;
describe('consumer handler', () => {
beforeAll(() => {
// stopConsumerMock = activityConsumer.stopConsumer = jest.fn().mockReturnValue(1);
stopConsumerMock = jest.fn().mockReturnValue(1);
});
beforeEach(() => {
jest.resetModules(); // important line
});
afterEach(() => {
stopConsumerMock.mockClear();
mockResponse.json.mockClear();
});
describe('stopConsumingHandler', () => {
it('Should return success true and not call stopConsumer when no consumer exists', () => {
// activityConsumer.consumer = undefined;
// mock by this way
jest.mock('../../src/utils/activity.consumer', () => ({
consumer: undefined,
stopConsumer: stopConsumerMock,
}));
const { stopConsumingHandler } = require('../../src/handlers/consumer.handlers');
stopConsumingHandler(mockRequest, mockResponse);
expect(stopConsumerMock.mock.calls.length).toEqual(0);
expect(mockResponse.json.mock.calls.length).toEqual(1);
expect(mockResponse.json).toHaveBeenCalledWith({ success: true });
});
it('Should return success true and call stopConsumer when consumer exists', () => {
// activityConsumer.consumer = true;
// mock by this way
jest.mock('../../src/utils/activity.consumer', () => ({
consumer: true, // mock value for consumer
stopConsumer: stopConsumerMock,
}));
const { stopConsumingHandler } = require('../../src/handlers/consumer.handlers');
stopConsumingHandler(mockRequest, mockResponse);
expect(stopConsumerMock.mock.calls.length).toEqual(1);
expect(mockResponse.json.mock.calls.length).toEqual(1);
expect(mockResponse.json).toHaveBeenCalledWith({ success: true });
});
});
});

How to call external function in jest

I'm New to unit test and trying to test my controller method.my project architecture design is as follow
Controller->Service->Model.
My test scenarios :
Pass correct parameters to controller method and test success response
Pass Invalid parameters to controller method and test error response
When i going to test scenario 1 ,according to my understanding i want to mock my programService and it return values.I have write test as follow and got errors.
I would really appreciate some one can fix this
ProgramsController.js
const ProgramService = require('../../services/program/programService');
class ProgramsController {
constructor() {
this.programService = new ProgramService();
}
async subscribe(req, res) {
try {
const { userId, uuid, msisdn, body: { programId } } = req;
const data = { userId, programId, msisdn, uuid }
const subscribe = await this.programService.subscribeUser(data);
res.json({
status: true,
message: 'Success',
friendly_message: constant.MSG.SUBSCRIPTION,
data: subscribe
})
} catch (error) {
res.status(500)
.json({
status: false,
message: 'Fail',
friendly_message: constant.MSG.SUBSCRIPTION_FAIL
})
}
}
}
ProgramService.js
class ProgramService {
constructor() {
this.subscriber = new Subscriber();
this.subsciberProgram = new SubsciberProgram()
}
async subscribeUser(data) {
try {
const { msisdn, userId, programId, uuid } = data;
...
return subscribedData;
} catch (error) {
throw error;
}
}
}
module.exports = ProgramService;
test.spec.js
const ProgramsService = require('../src/services/program/programService')
const ProgramsController = require('../src/controllers/programs/programsController')
const programController = new ProgramsController()
const programsService = new ProgramsService()
beforeAll(() => {
db.sequelize.sync({ force: true }).then(() => { });
});
const mockRequest = (userId, uuid, msisdn, body) => ({
userId,
uuid,
msisdn,
body,
});
const mockResponse = () => {
const res = {};
res.status = jest.fn().mockReturnValue(res);
res.json = jest.fn().mockReturnValue(res);
return res;
};
const serviceRecord = { userId: 1, programId: 1, msisdn: '56768382967', uuid: '46651a19-3ef1-4149-818e-9bd8a5f359ef' };
const fakeServiceReturn = { program_id: 1, amount: 5, no_of_questions: 10 }
describe('Subscribe', () => {
test('should return 200', async () => {
const req = mockRequest(
1,
'56768382967',
'46651a19-3ef1-4149-818e-9bd8a5f359ef',
{ 'programId': 1 }
);
const res = mockResponse();
const spy = jest.spyOn(programsService, 'subscribeUser').mockImplementation(() => serviceRecord);
await programController.subscribe(req, res);
expect(programsService.subscribeUser()).toHaveBeenCalledWith(fakeServiceReturn);
expect(res.status).toHaveBeenCalledWith(500);
expect(res.json).toHaveBeenCalledWith({
status: true,
message: 'Success',
friendly_message: 'successfull get data',
data : { program_id: 1, amount: 5, no_of_questions: 10 }
});
spy.mockRestore();
});
});
how can i mock programService.subscribeUser and test success response?
This mock should return a promise:
jest.spyOn(programsService, 'subscribeUser').mockImplementation(() => Promise.resolve(serviceRecord));

ApolloGraphQL: graphql() Not Activating Resolver?

I need to do a server-to-server graphQL call. Thanks to the advice received on this SO post, and also documented here, I'm approaching it like this:
async function () {
const {data, errors} = await graphql(
schema,
CRON_JOB_TO_FIND_USERS_WHO_HAVE_GONE_OFFLINE_MUTATION,
{},
{caller: 'synced-cron'},
{timeStarted: new Date().toISOString().slice(0, 19).replace('T', ' ')}
)
console.log('data', data)
console.log('errors', errors)
return true;
}
It's not throwing any errors, but it's returning null data:
Also, a debugger breakpoint in the resolver isn't being hit.
SCHEMA
cronJobToFindUsersWhoHaveGoneOffline(timeStarted: String): epUserData
QUERY
// note -- no gql``. This string is passed directly to graphql() function
// where it gets gql applied to it.
const CRON_JOB_TO_FIND_USERS_WHO_HAVE_GONE_OFFLINE_MUTATION = `
mutation ($timeStarted: String){
cronJobToFindUsersWhoHaveGoneOffline(timeStarted: $timeStarted){
id,
user_presence,
user_presence_time_of_last_update
},
}
`;
RESOLVER
cronJobToFindUsersWhoHaveGoneOffline(parent, args, context){
debugger; <== NEVER GETS ACTIVATED
return Promise.resolve()
.then(() => {
debugger;
//CODE TO FIND USERS AND MARK THEM AS BEING OFFLINE GOES HERE
return usersWhoWentOffline;
})
.then((usersWhoWentOffline) => {
debugger;
return usersWhoWentOffline;
})
.catch((err) => {
debugger;
console.log(err);
});
},
What am I missing?
It should work. Here is a completed working example:
server.ts:
import { ApolloServer } from 'apollo-server';
import { schema } from './schema';
const server = new ApolloServer({ schema });
export { server };
schema.ts:
import { gql, makeExecutableSchema } from 'apollo-server';
const typeDefs = gql`
type EpUserData {
id: ID!
user_presence: String
user_presence_time_of_last_update: String
}
type Query {
dummy: String
}
type Mutation {
cronJobToFindUsersWhoHaveGoneOffline(timeStarted: String): EpUserData
}
`;
const resolvers = {
Mutation: {
async cronJobToFindUsersWhoHaveGoneOffline(parent, args, context) {
const usersWhoWentOffline = { id: 1, user_presence: 'test', user_presence_time_of_last_update: '2020' };
return Promise.resolve()
.then(() => {
return usersWhoWentOffline;
})
.catch((err) => {
console.log(err);
});
},
},
};
const schema = makeExecutableSchema({ typeDefs, resolvers });
export { schema };
server.test.ts:
import { graphql } from 'graphql';
import { schema } from './schema';
import { server } from './server';
const CRON_JOB_TO_FIND_USERS_WHO_HAVE_GONE_OFFLINE_MUTATION = `
mutation ($timeStarted: String){
cronJobToFindUsersWhoHaveGoneOffline(timeStarted: $timeStarted){
id,
user_presence,
user_presence_time_of_last_update
},
}
`;
describe('62122142', () => {
beforeAll(async () => {
const { url } = await server.listen();
console.log(`server is listening on ${url}`);
});
afterAll(async () => {
await server.stop();
});
it('should pass', async () => {
const { data, errors } = await graphql(
schema,
CRON_JOB_TO_FIND_USERS_WHO_HAVE_GONE_OFFLINE_MUTATION,
{},
{ caller: 'synced-cron' },
{
timeStarted: new Date()
.toISOString()
.slice(0, 19)
.replace('T', ' '),
},
);
console.log('data', data);
console.log('errors', errors);
return true;
});
});
integration test result:
PASS apollo-graphql-tutorial src/stackoverflow/62122142/server.test.ts (7.143s)
62122142
✓ should pass (12ms)
console.log src/stackoverflow/62122142/server.test.ts:18
server is listening on http://localhost:4000/
console.log src/stackoverflow/62122142/server.test.ts:36
data [Object: null prototype] {
cronJobToFindUsersWhoHaveGoneOffline:
[Object: null prototype] {
id: '1',
user_presence: 'test',
user_presence_time_of_last_update: '2020' } }
console.log src/stackoverflow/62122142/server.test.ts:37
errors undefined
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 7.226s
source code: https://github.com/mrdulin/apollo-graphql-tutorial/tree/master/src/stackoverflow/62122142

Unit test for customPollingHook which uses apollo useLazyQuery

So I have written a custom polling hook which uses useContext and useLazyQuery hooks. I want to write a unit test for this, which should cover its returned values state and side effect.
So far I have managed to do this much but I'm not so sure how to proceed ahead. Any tips?
export const useUploadActivityPolling = (
teId: TeIdType
): UploadActivityPollingResult => {
const { dispatch, uploadActivityId }: StoreContextType = useAppContext();
const [fetchActivityStatus, { error: UploadActivityError, data: UploadActivityData, stopPolling }] = useLazyQuery(
GET_UPLOAD_ACTIVITY,
{
pollInterval: 3000,
fetchPolicy: 'network-only',
variables: { teId, activityId: uploadActivityId },
}
);
useEffect(() => {
if (UploadActivityData) {
setUploadActivityId(
UploadActivityData.getUploadActivityStatus.activity_id,
dispatch
);
updateActivityStateAction(UploadActivityData.getExcelUploadActivityStatus.status, dispatch);
}
}, [UploadActivityData]);
return { fetchActivityStatus, stopPolling, UploadActivityError };
};
import React from 'react';
import { mount } from 'enzyme';
const TestCustomHook = ({ callback }) => {
callback();
return null;
};
export const testCustomHook = callback => {
mount(<TestCustomHook callback={callback} />);
};
describe('useUploadActivityPolling', () => {
let pollingResult;
const teId = 'some id';
beforeEach(() => {
testCustomHook(() => {
pollingResult = useUploadActivityPolling(teId);
});
});
test('should have an fetchActivityStatus function', () => {
expect(pollingResult.fetchActivityStatus).toBeInstanceOf(Function);
});
});

Get Large Set of Data (~20000 item) from mongodb and display it in angular 7 Admin Products Page

I'm trying to get ~20000 items from Mongodb and display them at my Angular 7 Project in Admin Products Page in a table
The Problem is that the website takes too much time and sometimes it crashes
Is there a way to get them as 1000 item after another, get them fastly, or paginate them as 0-1000 item in a page 1 and 1000-2000 in page 2?
I searched for it and I didn't find any useful resource or even a similar question here.
I found that I could limit number of get items in mongodb through this code:
ITEMS_COLLECTION.find({}).limit(1000).toArray((err, allItems) => {
items = allItems
})
I don't want to just limit it to 1000, I want get all of them and display them without crashing the browser or not to be so slow.
This is the Item Page: src > Item.js
function getItems() {
let items
Server().then((server_data) => {
server_data.ITEMS_COLLECTION.find({}).limit(1000).toArray((err, allItems) => {
items = allItems
})
})
/*eslint no-undef: 0*/
return new Promise(resolve => {
setTimeout(() => {
resolve(items)
}, 4000)
})
}
This is the server page: src > server.js
app.get('/activeProducts', (req, res) => {
Item.getActiveItems()
.then(active_items => {
res.send(active_items);
})
.catch(err => {
throw new CustomError('Could not get Active Items', err);
});
});
This is the Products Service:
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { LoginService } from './login.service';
import { Router } from '#angular/router';
import { MatDialog, MatDialogRef } from '#angular/material';
import { environment } from '../../environments/environment';
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
Authorization: 'my-auth-token'
})
};
#Injectable()
export class ProductsService {
products = this.http.get(
` ${environment.link_url_with_backend}/activeProducts`
);
cached_products;
constructor(
private loginService: LoginService,
private router: Router,
private http: HttpClient,
public dialogRef: MatDialog
) {
this.products.subscribe(data => {
console.log(data);
this.cached_products = data;
});
}
}
This is the Products Component:
export class ProductsComponent implements OnInit, DoCheck {
constructor(private productService: ProductsService) {}
products;
ngOnInit() {
this.products = this.productService.cached_products;
}
}
This is the Products HTML:
<div
class="products-container wrapper"
>
<app-product
*ngFor="let product of products"
[product]="product"
style="width: 360px;"
></app-product>
</div>
First of All In The Backend you need to get the first 100 for example:
function getFirst100Items() {
let items
ITEMS_COLLECTION
.find({})
.limit(100)
.sort({id: 1})
.toArray( (err, allItems) => {
items = allItems
})
return new Promise(resolve => {
setTimeout(() => {
resolve(items)
}, 2000)
})
}
Then you can add load more function for example:
function getMore100Items(loadedItems) {
let items
server_data.ITEMS_COLLECTION
.find({ id: { $gte: loadedItems } })
.limit(100)
.sort({id: 1})
.toArray( (err, allItems) => {
items = allItems
})
return new Promise(resolve => {
setTimeout(() => {
resolve(items)
}, 2000)
})
}
function getItemsCount() {
let itemsCounts
server_data.ITEMS_COLLECTION.countDocuments()
.then( (counts) => {
itemsCounts = counts
})
return new Promise(resolve => {
setTimeout(() => {
resolve({itemsCounts})
}, 1000)
})
}
Then You Specify the express routes
app.get('/first/100products', (req, res) => {
Item.getFirst100Items()
.then(items => {
res.send(items);
})
.catch(err => {
throw new CustomError('Could not get Items', err);
});
});
app.post('/loadmore/products', (req, res) => {
loaded_items = req.body.loadedItems
res.send({loaded_items})
});
app.get('/loadmore/products', (req, res) => {
setTimeout(() => {
Item.getMore100Items(loaded_items)
.then(items => {
res.send(items);
})
.catch(err => {
throw new CustomError('Could not get Items', err);
});
}, 2000);
});
Second In Angular 7
Parent Component
loadedItems = 0;
#ViewChild(AdminTableComponent) adminTable;
constructor(public dialog: MatDialog, private http: HttpClient) {
this.http
.get(` ${environment.link_url_with_backend}/first/100products`)
.subscribe((data: {}[]) => {
this.products_data = data;
this.dataSource = new MatTableDataSource(this.products_data);
});
}
ngOnInit() {}
loadMore() {
this.http
.get(` ${environment.link_url_with_backend}/products/length`)
.subscribe((itemsCount: any) => {
if (this.loadedItems < itemsCount.itemsCounts - 100) {
this.adminTable.isLoad = true;
this.loadedItems += 100;
this.http
.post(
`${environment.link_url_with_backend}/loadmore/products`,
JSON.stringify({ loadedItems: this.loadedItems }),
httpOptions
)
.subscribe(data => {
console.log(data);
});
this.http
.get(` ${environment.link_url_with_backend}/loadmore/products`)
.subscribe((items: {}[]) => {
items.map(product => {
this.products_data.push(product);
this.dataSource = new MatTableDataSource(this.products_data);
this.adminTable.isLoad = false;
this.adminTable.dataSource.sort = this.adminTable.sort;
this.adminTable.dataSource.paginator = this.adminTable.paginator;
return;
});
});
} else {
this.adminTable.isLoad = false;
this.adminTable.isLoadMore = false;
alert('No More Products to Get');
return;
}
});
}
ChildComponent
loadMoreItems() {
this.loadMore.emit('loadMore');
}
#Input() dataSource;
#Input() displayedColumns;
#Input() dialogComponent;
#Output() loadMore = new EventEmitter();
isLoad = false;
isLoadMore = false;
And you can continue from here
Hope this helps!
Note: All this is just an example so don't take it exactly

Resources