How to mock Mongo find using Sinon and Proxyquire - unit-testing

I have the below Middleware class which I want to unit test:
const jwt = require('jsonwebtoken');
const config = require('../config/auth.config.js');
const db = require('../models');
const User = db.user;
const Role = db.role;
isAdmin = (req, res, next) => {
User.findById(req.userId).exec((err, user) => {
if (err) {
res.status(500).send({ message: err });
return;
}
Role.find(
{
_id: { $in: user.roles }
},
(err, roles) => {
console.log('Made it here Role');
console.log(JSON.stringify(roles));
console.log(roles);
if (err) {
console.log(err);
res.status(500).send({ message: err });
return;
}
for (let i = 0; i < roles.length; i++) {
if (roles[i].name === 'admin') {
next();
return;
}
}
res.status(403).send({ message: 'Require Admin Role!' });
return;
}
);
});
};
I'm able to mock the User.findById, the config the jsonwebtoken, but I'm not able to correctly stub the Role.find(...
This is my current test spec using Mocha and Chai, Sinon and Proxyquire
const chai = require('chai');
const proxyquire = require('proxyquire');
const sinon = require('sinon');
const mongoose = require('mongoose');
chai.should();
var expect = chai.expect;
describe('Verify AuthJWT class', () => {
let mockAuthJwt;
let userStub;
let roleStub;
let configStub;
let jwtStub;
let json;
let err;
let json1;
let err1;
before(() => {
userStub = {
exec: function (callback) {
callback(err, json);
}
};
roleStub = {
find: function (query, callback) {
console.log('Heree');
return callback(err1, json1);
}
};
configStub = {
secret: 'my-secret'
};
jwtStub = {
verify: function (token,
secretOrPublicKey, callback) {
return callback(err, json);
}
};
mockAuthJwt = proxyquire('../../../middlewares/authJwt.js',
{
'User': sinon.stub(mongoose.Model, 'findById').returns(userStub),
'db.role': sinon.stub().returns(roleStub),
'../config/auth.config.js': configStub,
'jsonwebtoken': jwtStub
}
);
});
describe('isAdmin function', () => {
it('should Pass when user is Admin', (done) => {
err = null;
json = { roles: ['5ef3bd3f4144ae5898347e4e'] };
err1 = {};
json1 = [{ _id: '5ef3bd3f4144ae5898347e4e', name: 'admin', __v: 0 }];
let fakeRes = {
status: sinon.stub().returnsThis(),
send: sinon.stub()
};
let fakeReq = {
body: { userId: '123', email: 'test#test.com', roles: ['admin'] }
};
let fakeNext = sinon.spy();
mockAuthJwt.isAdmin(fakeReq, fakeRes, fakeNext);
expect(fakeNext.calledOnce).to.be.true;
console.log('Status ' + fakeRes.status.firstCall.args[0]);
done();
});
Any inside on how to correctly use the proxyquire mock and stub the Role.find method so I can unit test the function correctly.

Based on your case (unit test), you do not need proxyquire at all. You just need chai and sinon.
This is simplified example on how can be done.
File middleware.js (just for example file name)
// #file: middleware.js (This is line 1)
const db = require('./models'); // Fake model.
const isAdmin = (req, res, next) => {
const User = db.user; // Define it inside.
const Role = db.role; // Define it inside.
User.findById(req.userId).exec((err1, user) => {
if (err1) {
res.status(500).send({ message: err1 });
return;
}
Role.find({ _id: { $in: user.roles } }, (err2, roles) => {
if (err2) {
res.status(500).send({ message: err2 });
return;
}
for (let i = 0; i < roles.length; i += 1) {
if (roles[i].name === 'admin') {
next();
return;
}
}
res.status(403).send({ message: 'Require Admin Role!' });
});
});
};
module.exports = { isAdmin };
File test / spec: isAdmin.test.js
const { expect } = require('chai');
const sinon = require('sinon');
// Load module under test.
const middleware = require('./middleware');
// Load module db to create stubs.
const db = require('./models');
describe('Verify AuthJWT class', function () {
describe('isAdmin function', function () {
it('should Pass when user is Admin', function (done) {
// Fake result user findById.
const fakeUser = { roles: ['5ef3bd3f4144ae5898347e4e'] };
const fakeErrUser = null;
// Create stub for User findById.
const stubUserFindByID = sinon.stub(db.user, 'findById');
stubUserFindByID.returns({
exec: (arg1) => {
// Inject fakeErrUser and fakeUser result here.
arg1(fakeErrUser, fakeUser);
},
});
// Fake result role find.
const fakeRole = [{ _id: '5ef3bd3f4144ae5898347e4e', name: 'admin', __v: 0 }];
const fakeErrRole = null;
// Create stub for Role find.
const stubRoleFind = sinon.stub(db.role, 'find');
stubRoleFind.callsFake((arg1, arg2) => {
// Inject fakeErrRole and fakeRole result here.
arg2(fakeErrRole, fakeRole);
});
// Create fake response: empty object because no activity.
const fakeRes = {};
// Create fake request.
// Note: I remove body property!
const fakeReq = { userId: '123', email: 'test#test.com', roles: ['admin'] };
// Create fake for next function (fake is sufficient).
const fakeNext = sinon.fake();
// Call function under test.
middleware.isAdmin(fakeReq, fakeRes, fakeNext);
// Verify stub user findById get called once.
expect(stubUserFindByID.calledOnce).to.equal(true);
// Make sure stub user findById called once with correct argument.
expect(stubUserFindByID.calledOnceWith(fakeReq.userId)).to.equal(true);
// Verify stub role find get called once.
expect(stubRoleFind.calledOnce).to.equal(true);
// Make sure stub role find called with correct argument.
// Note: alternative style.
expect(stubRoleFind.args[0][0]).to.deep.equal({
// Query use fakeUser result.
_id: { $in: fakeUser.roles },
});
// Finally for this case: make sure fakeNext get called.
expect(fakeNext.calledOnce).to.equal(true);
// Do not forget to restore the stubs.
stubUserFindByID.restore();
stubRoleFind.restore();
done();
});
});
});
Run it using nyc (to check coverage) and mocha (test runner).
$ npx nyc mocha isAdmin.test.js --exit
Verify AuthJWT class
isAdmin function
✓ should Pass when user is Admin
1 passing (7ms)
---------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------|----------|----------|----------|----------|-------------------|
All files | 77.27 | 50 | 60 | 76.19 | |
middleware.js | 73.68 | 50 | 100 | 72.22 | 10,11,15,16,26 |
models.js | 100 | 100 | 0 | 100 | |
---------------|----------|----------|----------|----------|-------------------|
$
That test case only cover success condition (next function get called). I hope the example clear enough and you can continue to create test cases to fully cover function isAdmin based on my example above. Only 3 cases left. Good luck!

Related

DynamoDB SDK async function returns undefined when using sinon

I am trying to use sinon to test a piece of code that is using an DynamoDB SDK method batchGet. Below the code:
const fetchSingleUser = async (userId) => {
try {
let queryParams = {RequestItems: {}};
queryParams.RequestItems['users'] = {
Keys: [{'UserId': userId}],
ProjectionExpression: 'UserId,Age,#UserName',
ExpressionAttributeNames: {'#UserName': 'Name'}
};
const res = await docClient.batchGet(queryParams).promise();
return res.Responses.users[0];
} catch (e) {
console.log('users::fetch::error - ', e);
}
};
Below the test using sinon:
'use strict';
const sinon = require('sinon');
const proxyquire = require('proxyquire').noCallThru();
let assert = require('assert');
describe('DynamoDB Mock Test', function () {
let AWS;
let scriptToTest;
let batchGetFunc;
before(function () {
batchGetFunc = sinon.stub();
AWS = {
DynamoDB: {
DocumentClient: sinon.stub().returns({
batchGet: batchGetFunc
})
}
};
scriptToTest = proxyquire('../index', {
'aws-sdk': AWS
});
});
it('Should scan using async/await and promise', async function () {
let result = { UserId: 'segf876seg876', Age: 33, Name: 'Paul' }
batchGetFunc.withArgs(sinon.match.any).returns({
promise: () => result
});
const data = await scriptToTest.fetchSingleUser('segf876seg876');
console.log('--data: ', data)
assert.equal(data.UserId, 'segf876seg876');
});
});
The Problem:
const data = await scriptToTest.fetchSingleUser('segf876seg876') always returns 'undefined'
Function fetchSingleUser always returns 'undefined' because you do not return anything after catch (after error happens). You only define return value on success.
But why errors happens, because const res does not contain Responses.users[0].
Simple solution:
change let result = { UserId: 'segf876seg876', Age: 33, Name: 'Paul' } to satisfy code Responses.users[0] to
const result = {
Responses: {
users: [{ UserId: 'segf876seg876', Age: 33, Name: 'Paul' }],
},
};
Note: use const if you not change variable value.

How to write unit test case for JWT strategy

I am new to passport.js and trying to cover the unit test case for my JWT strategy. Can anyone suggest how to do that?
// Setup JWT strategy for all requests
passport.use(
new JWTStrategy(
{
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: JWT_PRIVATE_KEY,
},
async (jwtPayload: any, done: any) => {
const isUser = jwtPayload.type === EntityType.User;
const model = isUser ? userModel : vendorModel;
try {
const document = await model.findOne({ _id: jwtPayload.id });
if (document) {
return done(null, jwtPayload);
} else {
return done(null, false);
}
} catch (err) {
return done(err, false);
}
},
),
);
Unit test solution:
index.ts:
import passport from 'passport';
import { Strategy as JWTStrategy, ExtractJwt } from 'passport-jwt';
import { userModel, vendorModel, EntityType } from './models';
const JWT_PRIVATE_KEY = 'secret 123';
passport.use(
new JWTStrategy(
{
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: JWT_PRIVATE_KEY,
},
async (jwtPayload: any, done: any) => {
console.log('123123');
const isUser = jwtPayload.type === EntityType.User;
const model = isUser ? userModel : vendorModel;
try {
const document = await model.findOne({ _id: jwtPayload.id });
if (document) {
return done(null, jwtPayload);
} else {
return done(null, false);
}
} catch (err) {
return done(err, false);
}
},
),
);
models.ts:
export enum EntityType {
User = 'User',
}
export const userModel = {
async findOne(opts) {
return 'real user document';
},
};
export const vendorModel = {
async findOne(opts) {
return 'real vendor document';
},
};
index.test.ts:
import { Strategy as JWTStrategy, ExtractJwt, VerifyCallback, StrategyOptions } from 'passport-jwt';
import passport from 'passport';
import { userModel, vendorModel } from './models';
jest.mock('passport-jwt', () => {
const mJWTStrategy = jest.fn();
const mExtractJwt = {
fromAuthHeaderAsBearerToken: jest.fn(),
};
return { Strategy: mJWTStrategy, ExtractJwt: mExtractJwt };
});
jest.mock('passport', () => {
return { use: jest.fn() };
});
describe('62125872', () => {
let verifyRef;
beforeEach(() => {
const mJwtFromRequestFunction = jest.fn();
(ExtractJwt.fromAuthHeaderAsBearerToken as jest.MockedFunction<
typeof ExtractJwt.fromAuthHeaderAsBearerToken
>).mockReturnValueOnce(mJwtFromRequestFunction);
(JWTStrategy as jest.MockedClass<any>).mockImplementation((opt: StrategyOptions, verify: VerifyCallback) => {
verifyRef = verify;
});
});
it('should verify using user model and call done with jwtpayload if user document exists', async () => {
const payload = { type: 'User', id: 1 };
const mDone = jest.fn();
jest.spyOn(userModel, 'findOne').mockResolvedValueOnce('mocked user document');
await import('./');
await verifyRef(payload, mDone);
expect(passport.use).toBeCalledWith(expect.any(Object));
expect(JWTStrategy).toBeCalledWith(
{ jwtFromRequest: expect.any(Function), secretOrKey: 'secret 123' },
expect.any(Function),
);
expect(ExtractJwt.fromAuthHeaderAsBearerToken).toBeCalledTimes(1);
expect(userModel.findOne).toBeCalledWith({ _id: 1 });
expect(mDone).toBeCalledWith(null, { type: 'User', id: 1 });
});
it("should verify using user model and call done with false if user document doesn't exist", async () => {
const payload = { type: 'User', id: 1 };
const mDone = jest.fn();
jest.spyOn(userModel, 'findOne').mockResolvedValueOnce('');
await import('./');
await verifyRef(payload, mDone);
expect(passport.use).toBeCalledWith(expect.any(Object));
expect(JWTStrategy).toBeCalledWith(
{ jwtFromRequest: expect.any(Function), secretOrKey: 'secret 123' },
expect.any(Function),
);
expect(ExtractJwt.fromAuthHeaderAsBearerToken).toBeCalledTimes(1);
expect(userModel.findOne).toBeCalledWith({ _id: 1 });
expect(mDone).toBeCalledWith(null, false);
});
// you can do the rest parts
});
Unit test results:
PASS stackoverflow/62125872/index.test.ts
62125872
✓ should verify using user model and call done with jwtpayload if user document exists (11ms)
✓ should verify using user model and call done with false if user document doesn't exist (2ms)
-----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------|---------|----------|---------|---------|-------------------
All files | 85 | 83.33 | 60 | 84.21 |
index.ts | 92.86 | 75 | 100 | 92.31 | 24
models.ts | 66.67 | 100 | 33.33 | 66.67 | 6,11
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 3.716s, estimated 10s
Using supertest to verify full cycle
import request from 'supertest';
import express from 'express';
import jwt from 'jsonwebtoken'
export const createAuthToken = (userId) => {
const body = {
type: EntityType.User,
id: userId,
};
return jwt.sign(body, JWT_PRIVATE_KEY);
};
// this function should configure express app
const appLoader = async app => {
(await import('../app/loaders/express')).expressLoader({ app }); // express bindings and routes
await import('./'); // passport config
}
describe('passport-jwt auth', () => {
const app = express();
const token = createAuthToken('user1')
beforeAll(async () => {
await appLoader({ app });
});
it('should verify auth', async () => {
jest.spyOn(userModel, 'findOne').mockResolvedValueOnce('mocked user document');
await request(app)
.get('/protected-endpoint')
.set('Authorization', `Bearer ${token}`)
.expect(200);
});
it('should verify auth - failure', async () => {
await request(app)
.get('/protected-endpoint')
.set('Authorization', `Bearer wrong-token`)
.expect(401);
});
});

How to mock typescript services and functions that use AWS resources?

I am having a Typescript backend structure and I want to create unit tests for all the functionalities. I am using JEST and aws-skd-mock for mocking AWS. I have tried some things but it seems I am not doing the right thing.
I have this service where I am getting a parameter from ParamterStore (amazon.service.ts):
import * as AWS from "aws-sdk";
export class AmazonService {
parameterStore: AWS.SSM;
constructor() {
this.parameterStore = new AWS.SSM();
}
async getParam(param) {
let self = this;
console.log('IN getPARAM', param);
return new Promise(function(resolve, reject){
self.parameterStore.getParameter({
Name: param,
WithDecryption: true
}, function (err, data) {
if (err) {
console.log('Error ', err);
return resolve({Error: 'ParameterNotFound'})
}
console.log('RES ', data.Parameter.Value);
return resolve(data.Parameter.Value)
})
})
}
}
Then, I mock whole amazon.service file, I mock SSM.getParameter with response in my test file (amazon.service.spect.ts):
import * as AWSMock from "aws-sdk-mock";
import * as AWS from "aws-sdk";
import {AmazonService} from "./amazon.service";
jest.mock('./amazon.service');
describe('amazon service mock', () => {
let amazonService: AmazonService;
it('should get Parameter from Parameter Store', async () => {
const ssmGetParameterPromise = jest.fn().mockReturnValue({
promise: jest.fn().mockResolvedValue({
Parameter: {
Name: 'NAME',
Type: 'SecureString',
Value: 'VALUE',
Version: 1,
LastModifiedDate: 1546551668.495,
ARN: 'arn:aws:ssm:eu-test-1:123:NAME'
}
})
});
AWSMock.setSDKInstance(AWS);
AWSMock.mock('SSM', 'GetParameter', ssmGetParameterPromise);
amazonService = new AmazonService();
console.log(await amazonService.getParam('NAME'))
await expect(amazonService.getParam('NAME')).resolves.toBe('VALUE')
})
});
With this I get undefined when amazonService.getParam is called.
As I looked in the examples they are initializing new AWS.SSM() right after is mocked and call it from test, but I want to achieve that by calling my function. It seems like SSM is not mocked when my function is called.
Any suggestions how to do this right ?
You don't need to mock ./amazon.service.ts module. Here is the unit test solution without using aws-sdk-mock.
E.g.
amazon.service.ts:
import * as AWS from 'aws-sdk';
export class AmazonService {
parameterStore: AWS.SSM;
constructor() {
this.parameterStore = new AWS.SSM();
}
async getParam(param) {
let self = this;
console.log('IN getPARAM', param);
return new Promise(function (resolve, reject) {
self.parameterStore.getParameter(
{
Name: param,
WithDecryption: true,
},
function (err, data) {
if (err) {
console.log('Error ', err);
return resolve({ Error: 'ParameterNotFound' });
}
console.log('RES ', data.Parameter!.Value);
return resolve(data.Parameter!.Value);
},
);
});
}
}
amazon.service.spec.ts:
import * as AWS from 'aws-sdk';
import { AmazonService } from './amazon.service';
import { mocked } from 'ts-jest/utils';
import { AWSError } from 'aws-sdk';
import { GetParameterResult } from 'aws-sdk/clients/ssm';
jest.mock('aws-sdk', () => {
const mSSMInstance = {
getParameter: jest.fn(),
};
const mSSM = jest.fn(() => mSSMInstance);
return { SSM: mSSM };
});
describe('amazon service mock', () => {
let amazonService: AmazonService;
it('should get Parameter from Parameter Store', async () => {
amazonService = new AmazonService();
expect(AWS.SSM).toBeCalled();
const mSSMInstance = new AWS.SSM();
const mData = {
Parameter: {
Name: 'NAME',
Type: 'SecureString',
Value: 'VALUE',
Version: 1,
LastModifiedDate: new Date(1995, 11, 17),
ARN: 'arn:aws:ssm:eu-test-1:123:NAME',
},
};
mocked(mSSMInstance.getParameter).mockImplementationOnce(
(params, callback?: (err: AWSError | null, data: GetParameterResult) => void): any => {
if (callback) {
callback(null, mData);
}
},
);
const actual = await amazonService.getParam('NAME');
expect(actual).toBe('VALUE');
});
});
unit test result with coverage report:
PASS stackoverflow/61871955/amazon.service.spec.ts (9.613s)
amazon service mock
✓ should get Parameter from Parameter Store (19ms)
console.log
IN getPARAM NAME
at AmazonService.<anonymous> (stackoverflow/61871955/amazon.service.ts:12:13)
console.log
RES VALUE
at stackoverflow/61871955/amazon.service.ts:24:19
-------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------------|---------|----------|---------|---------|-------------------
All files | 86.67 | 50 | 100 | 85.71 |
amazon.service.ts | 86.67 | 50 | 100 | 85.71 | 21-22
-------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 10.925s

Mocking ApolloClient's client.query method with Jest

Update January 22nd 2020
The solution from #slideshowp2 is correct, but I could not get it to work at all, due to this TypeError:
TypeError: Cannot read property 'query' of undefined
Well it turned out to be my jest configuration that had resetMocks: true set. After I removed it, the test did pass. (I don't know why though)
Original question:
I need to execute a graphql query in a helper function outside of a React component using Apollo Client and after a bit of trial and error I went for this approach which is working as it is supposed to:
setup.ts
export const setupApi = (): ApolloClient<any> => {
setupServiceApi(API_CONFIG)
return createServiceApolloClient({ uri: `${API_HOST}${API_PATH}` })
}
getAssetIdFromService.ts
import { setupApi } from '../api/setup'
const client = setupApi()
export const GET_ASSET_ID = gql`
query getAssetByExternalId($externalId: String!) {
assetId: getAssetId(externalId: $externalId) {
id
}
}
`
export const getAssetIdFromService = async (externalId: string) => {
return await client.query({
query: GET_ASSET_ID,
variables: { externalId },
})
return { data, errors, loading }
}
Now I am trying to write test tests for the getAssetIdFromService function, but I have trouble figuring out how to get the client.query method to work in tests.
I have tried the approach below including many others that did not work.
For this particular setup, jest throws
TypeError: client.query is not a function
import { setupApi } from '../../api/setup'
import { getAssetIdFromService } from '../getAssetIdFromService'
jest.mock('../../api/setup', () => ({
setupApi: () => jest.fn(),
}))
describe('getAssetIdFromService', () => {
it('returns an assetId when passed an externalId and the asset exists in the service', async () => {
const { data, errors, loading } = await getAssetIdFromService('e1')
// Do assertions
})
}
I assume I am missing something in relation to this part:
jest.mock('../../api/setup', () => ({
setupApi: () => jest.fn(),
}))
...but I cannot see it.
You didn't mock correctly. Here is the correct way:
getAssetIdFromService.ts:
import { setupApi } from './setup';
import { gql } from 'apollo-server';
const client = setupApi();
export const GET_ASSET_ID = gql`
query getAssetByExternalId($externalId: String!) {
assetId: getAssetId(externalId: $externalId) {
id
}
}
`;
export const getAssetIdFromService = async (externalId: string) => {
return await client.query({
query: GET_ASSET_ID,
variables: { externalId },
});
};
setup.ts:
export const setupApi = (): any => {};
getAssetIdFromService.test.ts:
import { getAssetIdFromService, GET_ASSET_ID } from './getAssetIdFromService';
import { setupApi } from './setup';
jest.mock('./setup.ts', () => {
const mApolloClient = { query: jest.fn() };
return { setupApi: jest.fn(() => mApolloClient) };
});
describe('59829676', () => {
it('should query and return data', async () => {
const client = setupApi();
const mGraphQLResponse = { data: {}, loading: false, errors: [] };
client.query.mockResolvedValueOnce(mGraphQLResponse);
const { data, loading, errors } = await getAssetIdFromService('e1');
expect(client.query).toBeCalledWith({ query: GET_ASSET_ID, variables: { externalId: 'e1' } });
expect(data).toEqual({});
expect(loading).toBeFalsy();
expect(errors).toEqual([]);
});
});
Unit test results with 100% coverage:
PASS apollo-graphql-tutorial src/stackoverflow/59829676/getAssetIdFromService.test.ts (8.161s)
59829676
✓ should query and return data (7ms)
--------------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
--------------------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
getAssetIdFromService.ts | 100 | 100 | 100 | 100 | |
--------------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 8.479s
Source code: https://github.com/mrdulin/apollo-graphql-tutorial/tree/master/src/stackoverflow/59829676
Use blow mock class:
class ApolloClient {
constructor(uri: string, fetch: any, request: any) {}
setupApi() {
return {
query: jest.fn(),
};
}
query() {
return jest.fn();
}
}
module.exports = ApolloClient;
and add below line to jest.cofig.ts
moduleNameMapper: {
'apollo-boost': '<rootDir>/.jest/appolo-client.ts',
},

How can I resolve AWS mock test with singleton classes

when I execute Jest in Javascript test with AWS mock via npm, it will be Failure.
because I use singleton class.
The difference like here.
「module.exports = Users;」 or 「module.exports = new Users();」
I guess AWS mock doesn't work with singleton class.
in that cause, how should I do to solve this problem?
'use strick';
var aws = require('aws-sdk')
aws.config.update({region:'ap-northeast-1'})
class Users {
constructor() {
this.table = 'Users'
this.dynamodb = new aws.DynamoDB()
}
getData(email) {
let params = {
TableName: this.table,
Key : { 'email': {'S':email} }
}
return this.dynamodb.getItem(params).promise()
}
}
// module.exports = Users // ← this will be success.
module.exports = new Users(); // ← this will be failure.
'use strict';
var aws = require('aws-sdk-mock'),
users = require('./user'),
chai = require('chai'),
path = require('path'),
should = chai.should(),
input = 'test#gmail.com',
usersObj;
aws.setSDK(path.resolve('node_modules/aws-sdk'));
describe('All Tests', function () {
// this.timeout(0);
beforeEach(function () {
aws.mock('DynamoDB', 'getItem', function (params, callback) {
callback(null, {Item: {email: params.Key.email.S}});
});
// usersObj = new users(); ← this will be success.
usersObj = users; // ← this will be failure.
});
it('getData', function (done) {
usersObj.getData(input).then(function (res) {
console.log(res);
res.Item.email.should.equal(input);
done();
});
});
});
This line:
module.exports = new Users();
...means that a Users object will get created as soon as the code runs...and it runs as soon as user.js is required.
This line:
users = require('./user')
...is at the top of your test file and this line:
aws.mock('DynamoDB', 'getItem', function (params, callback) {
callback(null, {Item: {email: params.Key.email.S}});
});
...is in a beforeEach...
...which means that user.js is required and runs before the mock has been created...which causes the test to fail.
If you are going to export an instance of Users then you just need to make sure you don't require the user.js file in your test until after you have set up your mock:
var aws = require('aws-sdk-mock'),
chai = require('chai'),
path = require('path'),
input = 'test#gmail.com',
usersObj;
chai.should()
aws.setSDK(path.resolve('node_modules/aws-sdk'));
describe('All Tests', function () {
beforeEach(function () {
aws.mock('DynamoDB', 'getItem', function (params, callback) {
callback(null, { Item: { email: params.Key.email.S } });
}); // <= set up the mock first...
usersObj = require('./user'); // <= ...then require user.js
});
it('getData', function (done) {
usersObj.getData(input).then(function (res) {
res.Item.email.should.equal(input); // Success!
done();
});
});
});
I could resolve this pattern too.
'use strict';
var aws = require('aws-sdk-mock'),
users = require('./user'),
chai = require('chai'),
path = require('path'),
should = chai.should(),
input = 'test#gmail.com',
usersObj;
const awsObject = require('aws-sdk');
aws.setSDK(path.resolve('node_modules/aws-sdk'));
describe('All Tests', function () {
// this.timeout(0);
beforeEach(function () {
aws.mock('DynamoDB', 'getItem', function (params, callback) {
callback(null, {Item: {email: params.Key.email.S}});
});
// it will be resolve problem by creating new AWS instance.
users.dynamodb = new awsObject.DynamoDB();
});
it('getData', function (done) {
users.getData(input).then(function (res) {
console.log(res);
res.Item.email.should.equal(input);
done();
});
});
});
You must call the aws client inside the class constructor
class MyClass {
constructor(){
this.dynamodb = new DynamoDB.DocumentClient({ region: "us-west-2" });
}
...
In the test file you must create a new instance of your class just after call de AWSMock. Example:
it('Should save on dinamoDB with param atributes void()', async () => {
AWSMock.mock('DynamoDB.DocumentClient', 'update', function (params, callback){
callback(null, { Attributes: { currentValue: 1 } } );
});
AWSMock.mock('DynamoDB.DocumentClient', 'put', function (params, callback){
callback(null, true);
});
const myClass = new MyClass();
...