Stub dependent function with sinon and proxyquire does not print stub result - javascript

I wrote a unit test for _getuser() but I don't see console.log print stub result.Also test coverage shows line 'let user = result.user;
console.log('User'+JSON.stringify(result));' is uncovered.Why stub result does not print in console log in getUser() function in LogInCommand class.
I see result shows undefined in unit test.
// userApi.js
'use strict';
const config = require('../../config/config');
const api = require('./apiService');
class UserApi {
constructor() {
}
getUser(userId) {
return api.get({
url: config.url,
qs: {
includeInactive: true,
id: userId,
startIndex: 0,
maxResults: 1
},
headers: {
Accept: 'application/json;',
'Connection': 'Keep-Alive'
}
});
}
}
module.exports = UserApi;
// LoginCommand.js
'use restrict';
const userApi = require('../../api/userApi');
class LogInCommand {
constructor() {
}
async _getUser(userId) {
let result = await new userApi().getUser(userId);
let user = result.user;
console.log('User'+JSON.stringify(result));
return user;
}
}
module.exports = LogInCommand;
//LoginCommand.test.js
describe('Test LogInCommand Class',function(){
it.only('_getUser function should return user', async ()=> {
let apiData= {
user:'abc'
};
let logincmd = proxyquire('../LogInCommand.js', {
userApi : { getUser : Promise.resolve(apiData) },'#noCallThru':true});
let obj= new logincmd();
let result= await obj._getUser(client);
});
});

The proxyquire configuration is incorrect in your current setup. Proxyquire maps the string value passed in a require call to the desired mock/stub values. Try the following instead:
let logincmd = proxyquire('../LogInCommand.js', {
'../../api/userApi' : { getUser : Promise.resolve(apiData) },
'#noCallThru':true
});

Below code worked for me
// userApi.js
'use strict';
const config = require('../../config/config');
const api = require('./apiService');
class UserApi {
constructor() {
}
getUser(userId) {
return api.get({
url: config.url,
qs: {
includeInactive: true,
id: userId,
startIndex: 0,
maxResults: 1
},
headers: {
Accept: 'application/json;',
'Connection': 'Keep-Alive'
}
});
}
}
module.exports = UserApi;
// LoginCommand.js
'use restrict';
const userApi = require('../../api/userApi');
class LogInCommand {
constructor() {
}
async _getUser(userId) {
let result = await new userApi().getUser(userId);
let user = result.user;
console.log('User'+JSON.stringify(result));
return user;
}
}
module.exports = LogInCommand;
//LoginCommand.test.js
describe('Test LogInCommand Class',function(){
it.only('_getUser function should return user', async ()=> {
class userApiStub{
constructor(){}
getUser() {
return Promise.resolve({
user:4
});
}
}
let logincmd = proxyquire('../LogInCommand.js', {
'../../api/userApi' :userApiStub },'#noCallThru':true});
let obj= new logincmd();
let result= await obj._getUser(client);
});
});

Related

Proxyquire not calling inner functions (npm modules) and does not work with classes properly

Where am i going wrong here?
Using mocha, chai, sinon and proxyquire for an express server and sequelize ORM linked with a postgres database
I am trying to test a login controller route from my express server
Before I show the file which I want to run my test on here is what "../services/authService.js" file looks like
../services/authService
const UserService = require("./userService");
module.exports = class AuthService extends UserService {
};
// so UserService will have the method findByEmail
// UserService class looks like this and it is coming from another file require("./userService.js) as stated above
/*
class UserService {
async findByEmail(email) {
try {
const user = await User.findOne({ where: { email: email }});
if (user) {
return user;
}
throw new Error("User not found");
} catch (err) {
err.code = 404;
throw err
}
}
}
*/
And here is the auth-controller.js file which I want to run the test on
auth-controller.js
const bcrypt = require('bcryptjs');
const AuthService = require("../services/authService"); // is a class which extends from another calls see the code above
const authService = new AuthService();
const jwtGenerator = require('../utils/jwtGenerator');
const createError = require("http-errors");
exports.loginRoute = async (req, res, next) => {
try {
req.body.password = String(req.body.password);
// db query trying to force a sinon.stub to resolve a fake value. But code wont pass here hence 500 error
const userQuery = await authService.findByEmail(req.body.email);
const compare = await bcrypt.compare(req.body.password, userQuery.password);
if (!compare) throw createError(401, 'Incorrect password.');
const user = {
id: userQuery.id, role: userQuery.is_admin ? "Administrator" : "User", email: userQuery.email, Authorized: true
}
const token = jwtGenerator(user);
return res
.cookie("access_token", token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
}).status(200).json({ message: "Logged in successfully 😊 👌", user, token });
} catch (error) {
next(error);
}
}
This code works in production but I cannot seem to test it. I used proxyquire to require the modules that the function uses. I have a big problem in making proxyquire work when it comes to my class AuthService here is my test file. As proxyquire is not working with classes some how. proxyquire is not using make AuthServiceMock at all cant figure out why.
First of these are my helper variables which I will use in the test file
../test-utils/user-helper
const createAccessToken = (payload) => jwt.sign(payload, TOKEN, {expiresIn: "1h"});
let loginDetail = {
email: "admin#test.com",
password: "123456"
};
let loginAdminUser = {
id: 1,
email: "admin#test.com",
password: "123456",
is_admin: true
}
const loginUser = {
id: 1,
email: "admin#test.com",
password: "123456",
is_admin: true
}
const adminUser = {
id: 1,
email: 'admin#test.com',
password: '123456',
is_admin: true,
first_name: 'john',
last_name: 'doe',
created_at: "2020-06-26T09:31:36.630Z",
updated_at: "2020-06-26T09:31:49.627Z"
}
module.exports = {
createAccessToken,
loginDetail,
loginAdminUser,
loginUser,
adminUser
}
And here is the test file I placed comments espcially around proxyquire when I am trying to use it as this is giving me some issues when it comes to using it with classes. And as well it is not calling mocked/stubbed npm modules for some reason
auth-controller.spec.js
"use strict";
const _ = require("lodash");
const path = require("path");
const proxyquire = require("proxyquire").noCallThru().noPreserveCache();
const chai = require("chai");
const { expect } = chai;
const sinon = require("sinon");
const sinonChai = require("sinon-chai");
chai.use(sinonChai);
// const AuthServiceOriginalClass = require("../../services/authService"); If i use this directly in proxyquire it calls the original class
const { createAccessToken, loginDetail, loginAdminUser, loginUser, adminUser } = require("../test-utils/user-helper");
const controllerPath = path.resolve('./controllers/authController.js');
describe("login route", () => {
let proxy, authService, bcryptStub, fakeCallback, fakeReq, fakeRes, fakeNext, resolveFn, token;
let result, bcryptStubbing, response;
class UserServiceMock {
async findByEmail(email) {
try {
if (email) {
return loginAdminUser;
}
} catch (error) {
throw error;
}
}
}
class AuthServiceMock extends UserServiceMock {};
bcryptStub = {
compare: function() { return true }
};
let tokeen = (kk) => {
return createAccessToken(kk);
}
// token = sinon.mock(createAccessToken(loginAdminUser)); // ?? which 1 to use?
token = sinon.spy(createAccessToken); // ?? which 1 to use?
// token = sinon.stub(createAccessToken) ?? which 1 to use?
proxy = proxyquire(controllerPath, {
"../services/authService.js": AuthServiceMock, // seems like this is not called at all
// "../services/authService.js": AuthServiceOriginalClass, // commented out if use this instead it calls the original class instant
"bcryptjs": bcryptStub,
"../utils/jwtGenerator": token,
// "#noCallThru": true // keep on or off?
});
before("Stub my methods", () => {
authService = new AuthServiceMock();
// If I call the entire loginRoute I want this stub authTry to be called inside of it and resolve that object value
authTry = sinon.stub(authService, "findByEmail").withArgs(loginDetail.email).resolves(loginAdminUser);
sinon.stub(bcryptStub, "compare").resolves(true); // force it to return true as that seems to be like the code of authController.js
// sinon.stub(token, "createAccessToken")
});
before("call the function loginRoute", async () => {
// fakeCallback = new Promise((res, rej) => {
// resolveFn = res
// });
fakeReq = {
body: {
email: loginDetail.email,
password: loginDetail.password
}
};
fakeRes = {
cookie: sinon.spy(),
status: sinon.spy(),
json: sinon.spy()
}
fakeNext = sinon.stub();
await proxy.loginRoute(fakeReq, fakeReq, fakeNext).then((_result) => {
result = _result;
});
console.log("result")
console.log(result) // undefined
console.log("result")
});
it("login route test if the stubs are called", async () => {
expect(authService.findByEmail).to.have.been.called // never called
// expect(bcryptStubbing).to.have.been.called // never called
// expect(response.status).to.deep.equal(200); // doesn't work
}).timeout(10000);
after(() => {
sinon.reset()
});
});
Where am i going wrong here in the test?

e is not a constructor - WalletConnectProvider model not working

Metamask is working perfectly.
BinanceChainWallet is working perfectly.
WalletConnect provider (model: QR - desktop/mobile wallet support), not working when I click on Wallet-Connect model on the front-end it give me this error only.
Platform on:
Framework: Laravel 8.0
JS
The problem is in this file (LaravelWeb3Scripts.php/walletconnect.ts) - this file is the one used for getting providers models.
Vendor/Provider used
https://github.com/sawirricardo/laravel-web3
Wallet-Connect Model repo https://github.com/WalletConnect/web3modal/blob/master/docs/providers/walletconnect.md
Console Log error (Chrome Developer Tools):
Uncaught (in promise) TypeError: e is not a constructor
at walletconnect.ts:31:22
at tslib.es6.js:100:1
at Object.next (tslib.es6.js:81:45)
at tslib.es6.js:74:1
at new Promise (<anonymous>)
at Module.A (tslib.es6.js:70:1)
at walletconnect.ts:15:44
at new Promise (<anonymous>)
at t.default (walletconnect.ts:15:10)
at e.<anonymous> (providers.ts:201:30)
walletconnect.ts - providers.ts - tslib.es6.js are imported from cdns
File: walletconnect.ts
import { IAbstractConnectorOptions, getChainId } from "../../helpers";
export interface IWalletConnectConnectorOptions
extends IAbstractConnectorOptions {
infuraId?: string;
rpc?: { [chainId: number]: string };
bridge?: string;
qrcode?: boolean;
}
const ConnectToWalletConnect = (
WalletConnectProvider: any,
opts: IWalletConnectConnectorOptions ) => {
return new Promise(async (resolve, reject) => {
let bridge = "https://bridge.walletconnect.org";
let qrcode = true;
let infuraId = "";
let rpc = undefined;
let chainId = 1;
console.log('wallet connect'); // todo remove dev item
if (opts) {
bridge = opts.bridge || bridge;
qrcode = typeof opts.qrcode !== "undefined" ? opts.qrcode : qrcode;
infuraId = opts.infuraId || "";
rpc = opts.rpc || undefined;
chainId =
opts.network && getChainId(opts.network) ? getChainId(opts.network) : 1;
}
const provider = new WalletConnectProvider({
bridge,
qrcode,
infuraId,
rpc,
chainId
});
try {
await provider.enable();
resolve(provider);
} catch (e) {
reject(e);
}
});
};
export default ConnectToWalletConnect;
File: LaravelWeb3Scripts.php
<?php
namespace Sawirricardo\LaravelWeb3\Components;
use Illuminate\View\Component;
class LaravelWeb3Scripts extends Component {
/**
* Create a new component instance.
*
* #return void
*/
public function __construct() {
//
}
/**
* Get the view / contents that represent the component.
*
* #return \Illuminate\Contracts\View\View|\Closure|string
*/
public function render() {
return <<<'blade'
<script>
class LaravelWeb3 {
constructor() {
this._provider = null;
this.reloadAfterFetching = true;
this.alertUserIfMetamaskIsNotInstalled = true;
this.contracts = #json(config('web3.contracts'));
this.web3ModalOptions = {
cacheProvider: true,
disableInjectedProvider: false,
providerOptions: {
binancechainwallet: {
package: true
},
walletconnect: {
package: WalletConnectProvider,
options: {
infuraId: "*********// hidden for post",
},
},
},
};
}
async onConnect() {
try {
const web3Modal = this.prepareWeb3Modal();
const provider = await web3Modal.connect();
provider.on("accountsChanged", async (accounts) => {
console.log("accountsChanged", accounts);
web3Modal.clearCachedProvider();
await this.fetchAccount(provider);
if (this.reloadAfterFetching) window.location.reload();
});
provider.on("chainChanged", async (chainId) => {
console.log("chainChanged", chainId);
web3Modal.clearCachedProvider();
await this.fetchAccount(provider);
if (this.reloadAfterFetching) window.location.reload();
});
provider.on("connect", ( { chainId }) => {
console.log("connect", chainId);
});
provider.on("disconnect", ( { code, message }) => {
console.log("disconnect", code, message);
});
await this.fetchAccount(provider);
if (this.reloadAfterFetching) window.location.reload();
} catch (e) {
console.log({ e });
}
}
async fetchAccount(web3Provider) {
const provider = new ethers.providers.Web3Provider(web3Provider);
const message = await(await(await fetch("/_web3/users/signature", {
method: "get",
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": "{{ csrf_token() }}",
},
})).json()).message;
await fetch("/_web3/users",{
method:'post',
body: JSON.stringify({
signature: await provider.getSigner().signMessage(message),
address: await provider.getSigner().getAddress(),
}),
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": "{{ csrf_token() }}",
},
})
this._provider = provider;
}
async onDisconnect() {
this._provider = null;
const web3Modal = this.prepareWeb3Modal();
await web3Modal.clearCachedProvider();
await fetch("/_web3/users/logout",{
method: "delete",
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": "{{ csrf_token() }}",
},
});
if (this.reloadAfterFetching) window.location.reload();
}
async getProvider() {
if (!this._provider) {
const web3Modal = this.prepareWeb3Modal();
const web3Provider = await web3Modal.connect();
this._provider = new ethers.providers.Web3Provider(web3Provider);
}
return this._provider;
}
prepareWeb3Modal () {
let Web3Modal, WalletConnectProvider;
// if (!(window.web3 || window.ethereum) && this.alertUserIfMetamaskIsNotInstalled) {
// alert(`Please install Metamask first`);
// return;
// }
if (window.Web3Modal.default) {
Web3Modal = window.Web3Modal.default;
}
if (window.WalletConnectProvider) {
WalletConnectProvider = window.WalletConnectProvider.default;
}
const web3Modal = new Web3Modal(this.web3ModalOptions);
return web3Modal;
}
addContract(address,contract) {
this.contracts = [...this.contracts, { address, contract }];
}
}
window.laravelWeb3 = new LaravelWeb3();
</script>
blade;
}
}

Stub out node-vault import

How can the 'node-vault' module be stubbed out using Sinon.JS?
Here is the sample code to be tested:
const NodeVault = require('node-vault');
class BearerAuth {
getVaultConfig (token) {
return {
apiVersion: 'v1',
endpoint: app.get('vault'),
token: token
};
}
verify (token) {
const vault = NodeVault(this.getVaultConfig(token));
const resp = vault.tokenLookupSelf().then((result) => {
console.log(`Vault token: ${token}, successfully authenticated.`);
return Promise.resolve({
'name': result.data.meta.username
});
}).catch((err) => {
console.error(err.message);
return Promise.reject(new this.ApplicationError.Authentication('Bearer token is incorrect.'));
});
return resp;
}
}
module.exports = BearerAuth;
}
The test code which attempts to stub out the module using sinon.stub:
const assert = require('assert');
const sinon = require('sinon');
const nodeVault = require('node-vault');
const BearerAuth = require('bearer');
describe('Bearer AuthConfig', () => {
beforeEach(async () => {
class TestBearerAuth extends BearerAuth {
// override some other methods
}
testAuth = new TestBearerAuth();
const vaultConfig = {
endpoint: 'http://localhost:8200',
token: '123'
};
testAuth.getVaultConfig = sinon.stub.returns(vaultConfig);
});
it('returns user when valid token', async () => {
const user = await testAuth.verify(null, 'mytoken');
assert.deepEqual(user, {name: 'sre'});
})
}
This fails when run with this error:
1) Bearer Auth
returns user when valid token:
TypeError: Attempted to wrap undefined property undefined as function
at wrapMethod (node_modules/sinon/lib/sinon/util/core/wrap-method.js:70:21)
at TestBearerAuth.stub [as getVaultConfig] (node_modules/sinon/lib/sinon/stub.js:65:44)
at TestBearerAuthConfig.verify (src/modules/auth/bearer.js:34:34)
at Context.<anonymous> (test/unit/modules/auth/test-bearer.js:57:39)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
The node-vault library has the following export in index.d.ts:
declare function NodeVault(options?: NodeVault.VaultOptions): NodeVault.client;
export = NodeVault;
The unit test code from the node-vault library provided the solution:
Test code:
const assert = require('assert');
const sinon = require('sinon');
const nodeVault = require('node-vault');
const BearerAuth = require('bearer');
describe('Bearer AuthConfig', () => {
beforeEach(async () => {
request = sinon.stub();
response = sinon.stub();
response.statusCode = 200;
response.body = {
data: {
meta: {
username: 'jack'
}
}
};
request.returns({
then (fn) {
return fn(response);
},
catch (fn) {
return fn();
}
});
class TestBearerAuth extends BearerAuth {
getVaultConfig (token) {
return {
endpoint: 'http://localhost:8200',
token: '123',
namespace: 'test',
'request-promise': {
defaults: () => request // dependency injection of stub
}
};
}
}
testAuth = new TestBearerAuth();
});
it('returns user when valid token', async () => {
const user = await testAuth.verify(null, 'mytoken');
assert.deepEqual(user, {name: 'sre'});
})
}
Adding this here in case someone else runs into a similar issue.

mock dynamodb partiql using jest

I didn't find a way to mock the new feature of dynamodb "PartiQL" defined in a lambda function.
In PartiQL we can use executeStatement to run sql like queries which are a kind of DynamoDBCustomizations.
But he configuration below return an error:
UnrecognizedClientException: The security token included in the
request is invalid
which means that the configuration of jest-mock is not correct.
This is the code to be tested
'use strict';
var ddb = new AWS.DynamoDB({ apiVersion: '2012-08-10' });
const TABLE_NAME = process.env.TABLE_NAME;
async function queryTest() {
let params = {
Statement: `select * from ${TABLE_NAME}`
};
let statementResult = await ddb.executeStatement(params).promise();
return statementResult.Items;
}
module.exports.get = async (event) => {
var result = queryTest();
if (!result) {
return {
statusCode: 200,
body: JSON.stringify(result)
};
}
else {
return {
statusCode: 400,
body: JSON.stringify(result)
};
}
};
and the testing script:
'use strict';
const AWS = require('aws-sdk');
const jestPlugin = require('serverless-jest-plugin');
const lambdaWrapper = jestPlugin.lambdaWrapper;
const mod = require('./handler');
function mock_aws() {
let mockDynamoDBClient = {
executeStatement: {
promise: jest.fn()
},
promise: jest.fn(),
};
jest.mock("aws-sdk", () => {
const config = {
update: jest.fn(),
};
return {
DynamoDB: jest.fn().mockImplementation(() => {
return {
executeStatement: () => mockDynamoDBClient.executeStatement,
};
}),
config: config
};
});
return mockDynamoDBClient;
}
describe('test', () => {
const OLD_ENV = process.env;
var mDynamoDb;
var wrapped;
beforeAll(async () => {
jest.resetModules();
wrapped = await lambdaWrapper.wrap(mod, { handler: 'get' });
process.env = { ...OLD_ENV };
process.env.TABLE_NAME = "TEST";
mDynamoDb = mock_aws();
});
test('implement tests here', async (done) => {
const mResult = [{ 'TEST': 1 }] ;
const response = await wrapped.run({})
expect(response).toEqual({
"statusCode": 200,
"body": JSON.stringify(mResult)
});
done();
});
});

How to check test case response using jasemine?

I am writing test case for API where I have passed params and main file method is calling http request to get the data, So core() is basically calling the API and getting the response. This is just backend code that we have to test using jasemine.
First how to see response in test case if that's matched with success that is defined in test.
What is a correct way to write test in below scenario.
balance.spec.ts
import { GetAccountBalance } from './GetAccountBalance.node'
import { Promise } from 'es6-promise'
import { HttpRequest } from '../../../core/http/HttpRequest.node'
import * as sinon from 'sinon'
import { getValidationError } from '../../../common/ValidationErrorFactory'
// for easy mocking and cleanup
const sandbox = sinon.createSandbox()
afterAll(function afterTests () {
sandbox.restore()
})
describe('getAccountBalance', function () {
// the module under test
const module = new GetAccountBalance()
const testURL = 'https://essdd.max.com:4535'
const urlPath = '/webServices/instrument/accountBalance'
const fullURL = testURL + urlPath
const options = { isJSON: true }
let result
const stubbedEC = sandbox.spy(getValidationError)
const stubbedHttp = sandbox.createStubInstance(HttpRequest)
const success = {
header: {
serviceName: 'accountBalance',
statusCode: '0000',
statusDesc: 'SUCCESS',
},
response: {
balanceAccount: '6346.44',
},
}
const params = { Id: '21544', appName: 'instrucn', channelName: 'Web' }
describe('core() is called with correct params', function () {
beforeEach(function () {
result = module.core(params, stubbedHttp)
})
it('it should return the response from server', function () {
// Invoke the unit being tested as necessary
result.then((data) => {
expect(data).toBe(success)
})
})
})
})
getaccountBalnce.ts
public core(args: IAccountBalanceParam, httpRequest: HttpRequestBase): Promise<any> {
console.log('enter core');
const DBPLurl: string = this.constructDBPLurl(args);
const DBPLrequest: IAccountBalanceDBPLParam = this.constructDBPLrequest(args);
return Promise.resolve(httpRequest.makeRequest(DBPLurl, DBPLrequest, {isJSON: true}));
}

Categories

Resources