I wrting API test in Mocha JS, have two files: 00_auth.js and utils.js
I want to create function in utils which is available in other files from same folder
utils.js
utils = {
randomStringGenerator: function (){
return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 7);
}
};
and test js
const chai = require('chai');
const mocha = require('mocha');
const config = require('../config');
const request = require('supertest')(url);
const assert = chai.assert;
const utils= require('./utils')
randomFirstName=utils.randomStringGenerator();
//randomLastName=stringGenerator();
randomMail=randomFirstName+'#'+randomLastName+'.pl';
auth_token = ''
describe('0_auth', () => {
it('should return token for unauthorized user', function(done) {
request
.post('/rest/v1/auth/get-token')
.set(config.headers)
.send({
"deviceUuidSource": "DEVICE",
"source" : "KIOSK",
"deviceUuid" : "uniquedeviceuuid"
})
.end(function(err,res){
assert.equal(res.status,200)
assert.property(res.body, 'token')
assert.isString(res.body.token)
auth_token=res.body.token
console.log('unathorized token: '+auth_token)
done(err);
});
});
});
I took this solution from Global functions in javascript, but it's not working, gives me Type Error :utils.randomStringGenerator is not a function
OK, got it, thanks
module.exports.randomStringGenerator = function (){
return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 7);
}
Related
**Edit: Re-written with a simple example that works first:
So I've got a test file and 2 modules.
moduleA has a dependency, moduleB
// moduleA.js
const ModuleB = require('./moduleB');
function functionA() {
return 20 + ModuleB.functionB();
};
module.exports = { functionA };
// moduleB.js
const functionB = () => {
return 10;
}
module.exports = { functionB }
My test file stubs out functionB (returned from moduleB) using proxyquire:
const sinon = require('sinon');
const proxyquire = require('proxyquire');
describe('Unit Tests', function() {
it('should work', () => {
const mockedFn = sinon.stub();
mockedFn.returns(30);
const copyModuleA = proxyquire('./moduleA', {
'./moduleB': {
functionB: mockedFn
}
});
console.log(copyModuleA.functionA());
})
});
So it outputs 50 (stubbed functionB 30 + functionA 20)
Now I'm trying to take this example into my code:
moduleA in this case is a file called validation.js. It is dependent on moduleB, in this case a sequelize model, Person, with the function I want to mock: findOne
validation.js exports module.exports = { validateLogin };, a function that calls validate, which returns a function that uses Person.findOne()
So in my mind, as with the simple example, I need to create a stub, point to the validation module in proxyquire, and reference the dependency and its findOne function. Like this:
const stubbedFindOne = sinon.stub();
stubbedFindOne.resolves();
validationModule = proxyquire('../../utils/validation', {
'../models/Person': {
findOne: stubbedFindOne
}
});
This should stub Person.findOne in validation.js. But it doesn't seem to. And I have no idea why.
let validationModule;
describe('Unit Tests', () => {
before(() => {
const stubbedFindOne = sinon.stub();
stubbedFindOne.resolves();
validationModule = proxyquire('../../utils/validation', {
'../models/Person': {
findOne: stubbedFindOne
}
});
})
it.only('should return 422 if custom email validation fails', async() => {
const wrongEmailReq = { body: {email: 'nik#hotmail.com'} };
const res = {
statusCode: 500,
status: (code) => {this.statusCode = code; return this},
};
const validationFn = validationModule.validateLogin();
const wrongEmail = await validationFn(wrongEmailReq, res, ()=>{});
expect(wrongEmail.errors[0].msg).to.be.equal('Custom Authorisation Error');
return;
})
And this is my validation.js file:
const Person = require('../models/Person');
// parallel processing
const validate = validations => {
return async (req, res, next) => {
await Promise.all(validations.map(validation => validation.run(req)));
const errors = validationResult(req);
if (errors.isEmpty()) {
return next();
}
const error = new Error();
error.message = process.env.NODE_ENV === 'development'? 'Validation Failed':'Error';
error.statusCode = !errors.isEmpty()? 422:500;
error.errors = errors.array({onlyFirstError: true});
next(error);
return error;
};
};
const validateLogin = () => {
const validations = [
body('email')
.isString()
// snip
.custom(async (value, {req}) => {
try{
const person = await Person.findOne({ where: { email: value } });
if(!person) return Promise.reject('Custom Authorisation Error');
} catch(err) {
throw err;
}
})
.trim(),
];
return validate(validations);
}
module.exports = {
validateLogin
};
So the code in both the small sample and my app is correct, apart from how I stub the function. It shouldn't resolve or reject anything (I tried both out of desperation). It should return null in order to satisfy the conditional rather than jump to the catch block:
try{
const person = await Person.findOne({ where: { email: value } });
if(!person) return Promise.reject('Custom Authorisation Error');
} catch(err) {
throw err;
}
Hope the simple example helps someone else with proxyquire though
In my code, I am using the library jsonwebtoken. To expedite the testing process, I would like to replace the library with a fake dependency using testdouble.
To test the creation of my fake dependency, I replaced the functionality of the sign method to just return a basic string. However, I noticed it is not working and is still running the original jsonwebtoken method.
What am I doing wrong here?
main.js
const jwt = require('jsonwebtoken');
const signHere = (id) => {
const token = jwt.sign({ id }, 'fakesignature');
return token;
}
module.exports = { signHere }
unit.test.js
const { expect } = require('chai');
const td = require('testdouble');
describe('Replace the jsonwebtoken library', function () {
let subject;
before(function () {
subject = require('./main.js');
const fakeJWT = td.replace('jsonwebtoken');
fakeJWT.sign = td.function();
td.when(fakeJWT.sign(td.matchers.anything, td.matchers.anything)).thenReturn('test');
})
it('should replace the return value', function() {
const value = subject.signHere(1);
expect(value).to.equal('test');
})
})
I'm trying to test a rest api which calls an external service.
server.js:
const express = require('express');
const app = express();
const router = express.Router();
const redirectUrl = require('../utils/redirection')
let baseUrl = 'myUrl';
let externalUrl = 'externalUrl';
router.get('/redirect', async (req, res) => {
const { productName } = req.query;
baseUrl = baseUrl + '/' + productName;
externalUrl = externalUrl + '/' + productName;
await redirectUrl(res)(timeOut, externalUrl, baseUrl)
})
app.use(router);
app.listen(3000);
utils/redirection.js:
edirectUrl = res => (timeOut, url, redirectUrl) => {
return new Promise((resolve, reject) => {
let cleared = false;
const timer = setTimeout(() => {
cleared = true;
return resolve(res.redirect(302, redirectUrl));
}, timeout);
return fetch(url)
.then(
response => {
if (!cleared) {
clearTimeout(timer);
const {location} = response.headers;
return resolve(res.redirect(302, location));
}
return null;
})
.catch(err => {
if (!cleared) {
clearTimeout(timer);
return reject(err);
}
});
});
}
test.js:
const requrest = require('request');
const chai = require('chai');
const server = require('../server/server');
const { expect } = chai;
describe('My test', () => {
it('should redirects to the suitable page', () => {
nock('url/to/external/service')
.get('/${productName}')
.reply(302, {
headers: {
location: 'this the page location'
}})
const { status, headers } = request(app).get('/redirect')
expect(status).to.equal(302);
expect(headers.location).to.not.equal(0);
})
})
When I execute the test, the request launches the API call. Then the redirectUrl was called inside. But nock does not intercept the request and the server call the external api. Does nock could intercept a depth HTTP request? or I missed something in my code? Does any suggestion please to resolve this problem?
Without a running example it's hard to tell what is going wrong. But I can tell there's a few things you can change here (I was also struggling with Nock today).
First, since you're dealing with redirects, take a look at this comment. This worked for me: https://github.com/nock/nock/issues/147#issuecomment-71433752
Second, pay attention: the syntax to specify headers is wrong, this is the correct one:
nock('url/to/external/service')
.get('/${productName}')
.reply(302, 'whatever you want', {
location: 'http://the.other.page.location'
})
Third: split your server.js file into two: server.js and app.js.
app.js
const app = express()
...
module.exports = app
server.js
const app = require('./path/to/app.js')
...
app.listen(3000)
This way you can import your app.js into your test suite without the listen part, which will avoid problems with network ports when new tests are run.
Let me know if this helped...
I have the following Firebase Function that makes use of Auth0 to get a user profile.
'use strict';
const {
dialogflow,
Image,
} = require('actions-on-google')
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
// database collection and key names
const DB_BANK_COLLECTION_KEY = 'bank'
// the action name from all Dialogflow intents
const INTENT_WELCOME_USER = 'Default Welcome Intent';
// Initialize the Auth0 client
var AuthenticationClient = require('auth0').AuthenticationClient;
var auth0 = new AuthenticationClient({
domain: functions.config().familybank.auth0.domain,
clientID: functions.config().familybank.auth0.clientid
});
const app = dialogflow();
app.intent(INTENT_WELCOME_USER, async (conv) => {
console.log('Request: ' + JSON.stringify(conv.request));
const userInfo = await auth0.getProfile(conv.user.access.token)
.catch( function(err) {
console.error('Error getting userProfile from Auth0: ' + err);
conv.close("Something went wrong. Please try again in a few minutes. " + err)
});
console.log('userInfo: ' + JSON.stringify(userInfo));
// check for existing bank, if not present, create it
var bankRef = db.collection(DB_BANK_COLLECTION_KEY).doc(userInfo.email);
const bankSnapshot = await bankRef.get()
})
exports.accessAccount = functions.https.onRequest(app);
I tried to mock auth0 in my tests using the following code (and several permutations), but the actual function always gets called instead of the mock.
const chai = require('chai');
const assert = chai.assert;
const sinon = require('sinon');
// Require firebase-admin so we can stub out some of its methods.
const admin = require('firebase-admin');
const test = require('firebase-functions-test')();
var AuthenticationClient = require('auth0').AuthenticationClient;
var auth0 = new AuthenticationClient({
domain: "mock",
clientID: "mock"
});
describe('Cloud Functions', () => {
let myFunctions, adminInitStub;
before(() => {
test.mockConfig({"familybank": {"auth0": {"domain": "mockdomain", "clientid": "mockid"}}});
adminInitStub = sinon.stub(admin, 'initializeApp');
sinon.stub(admin, 'firestore')
.get(function() {
return function() {
return "data";
}
});
sinon.stub(auth0, 'getProfile').callsFake( function fakeGetProfile(accessToken) {
return Promise.resolve({"email": "daniel.watrous#gmail.com", "accessToken": accessToken});
});
myFunctions = require('../index');
});
after(() => {
adminInitStub.restore();
test.cleanup();
});
describe('accessAccount', () => {
it('should return a 200', (done) => {
const req = {REQUESTDATA};
const res = {
redirect: (code, url) => {
assert.equal(code, 200);
done();
}
};
myFunctions.accessAccount(req, res);
});
});
})
Is there some way to mock auth0 for my offline tests?
I discovered that rather than initialize the Auth0 AuthenticationClient, I could first require the UsersManager, where the getProfile (which wraps getInfo) is defined.
var UsersManager = require('auth0/src/auth/UsersManager');
In my before() method, I can then create a stub for getInfo, like this
sinon.stub(UsersManager.prototype, 'getInfo').callsFake( function fakeGetProfile() {
return Promise.resolve({"email": "some.user#company.com"});
});
All the calls to auth0.getProfile then return a Promise that resolves to the document shown in my stub fake function.
Trying to write a unittest for the below module in /utility/sqsThing.js. However I'm having diffuculty mocking the sqs.sendMessage method. Anyone know how I should go about this. I'm using the sinon library, and mocha for running the tests.
The function that I'm trying to unittest utility/sqsThing.js:
const AWS = require('aws-sdk');
AWS.config.update({ region: 'us-east-1' });
const sqs = new AWS.SQS({ apiVersion: '2012-11-05' });
const outputQueURL = 'https:awsUrl';
const SQSOutputSender = (results) => {
const params = {
MessageBody: JSON.stringify(results),
QueueUrl: outputQueURL,
};
// Method that I want to mock
sqs.sendMessage(params, function (err, data) {
if (err) {
console.log('Error');
} else {
console.log('Success', data.MessageId);
}
});
};
My attempt at mocking the sqs.sendMessage method in a unittest sqsThingTest.js:
const sqsOutputResultSender = require('../utility/sqsThing');
const AWS = require('aws-sdk');
const sqs = new AWS.SQS({ apiVersion: '2012-11-05' });
const mochaccino = require('mochaccino');
const { expect } = mochaccino;
const sinon = require('sinon');
describe('SQS thing test', function() {
beforeEach(function () {
sinon.stub(sqs, 'sendMessage').callsFake( function() { return 'test' });
});
afterEach(function () {
sqs.sendMessage.restore();
});
it('sqsOutputResultSender.SQSOutputSender', function() {
// Where the mock substitution should occur
const a = sqsOutputResultSender.SQSOutputSender('a');
expect(a).toEqual('test');
})
});
Running this unittest with mocha tests/unit/sqsThingTest.js however I get:
AssertionError: expected undefined to deeply equal 'test'.
info: Error AccessDenied: Access to the resource https://sqs.us-east-1.amazonaws.com/ is denied..
It looks like the mock did not replace the aws api call. Anyone know how I can mock sqs.SendMessage in my test?
You could use rewire js it is a library that lets you inject mocked properties into your module you want to test.
Your require statement would look something like this:
var rewire = require("rewire");
var sqsOutputResultSender = rewire('../utility/sqsThing');
Rewire will allow you to mock everything in the top-level scope of you sqsThing.js file.
Also you need to return the value of sqs.sendMessage this will remove the issue expected undefined to deeply equal 'test'
Your original file would look the same just with a return statement.
//utility/sqsThing.js
const AWS = require('aws-sdk');
AWS.config.update({ region: 'us-east-1' });
const sqs = new AWS.SQS({ apiVersion: '2012-11-05' });
const outputQueURL = 'https:awsUrl';
const SQSOutputSender = (results) => {
const params = {
MessageBody: JSON.stringify(results),
QueueUrl: outputQueURL,
};
// Method that I want to mock
return sqs.sendMessage(params, function (err, data) {
if (err) {
console.log('Error');
} else {
console.log('Success', data.MessageId);
}
});
};
You would then write your unit test as follows:
//sqsThingTest.js
var rewire = require("rewire");
var sqsOutputResultSender = rewire('../utility/sqsThing');
const mochaccino = require('mochaccino');
const { expect } = mochaccino;
const sinon = require('sinon');
describe('SQS thing test', function() {
beforeEach(function () {
sqsOutputResultSender.__set__("sqs", {
sendMessage: function() { return 'test' }
});
});
it('sqsOutputResultSender.SQSOutputSender', function() {
// Where the mock substitution should occur
const a = sqsOutputResultSender.SQSOutputSender('a');
expect(a).toEqual('test');
})
});
This example returns an object with a property of sendMessage but this could be replaces with a spy.
Rewire Docs
Try moving the declaration of sqsOutputResultSender after you have stubbed the sendmessage function
var sqsOutputResultSender;
const AWS = require('aws-sdk');
const sqs = new AWS.SQS({ apiVersion: '2012-11-05' });
const mochaccino = require('mochaccino');
const { expect } = mochaccino;
const sinon = require('sinon');
describe('SQS thing test', function() {
beforeEach(function () {
sinon.stub(sqs, 'sendMessage').callsFake( function() { return 'test' });
sqsOutputResultSender = require('../utility/sqsThing');
});
afterEach(function () {
sqs.sendMessage.restore();
});
it('sqsOutputResultSender.SQSOutputSender', function() {
// Where the mock substitution should occur
const a = sqsOutputResultSender.SQSOutputSender('a');
expect(a).toEqual('test');
})
});