provide data for protractor test - node.js

I'm new to nodejs and working now to automatize functionnal test doing BDD with cucumber and protractor.
For some of my steps, i need to send query to an oracle database and then use the result to make a search in the tested website.
1- I'm trying with oracleDB witch returns me the expected result that i put in a variable but this one is not available in my steps. Method sendKeys of webdriver puts "undefined" in the input.
2- I'm also wondering if there's not another way because it was hard to install oracledb and the next task is to have jenkins builds
here is my code:
var dbQueryContract = function() {
var oracledb = require('oracledb');
//Database communication
oracledb.getConnection(
{
user : "xxx",
password : "xxx",
connectString : "xxx"
},
function(err, connection)
{
if (err) {
console.error(err.message);
return;
}
connection.execute(
"select xxx, xxx " +
"FROM xxxxx " +
"where xxx is not null and rownum < 5",
{
resultSet: true
},
// bind value for :id [110],
function(err, result)
{
if (err) { console.error(err.message); return; }
console.log(result.rows[0][0]);
});
criteria= result.rows[0][0];
// the connection is ok and i can log result.rows[0][0] i want to use for search
});
};
module.exports = new dbQueryContract();
************************************************************************************************
// Use the external Chai As Promised to deal with resolving promises in
// expectations.
var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
var expect = chai.expect;
var page1 = require('../page1.js');
var page2 = require('../page2.js');
var dbQueryContract = require('../dbQueryContract.js');
var EC = protractor.ExpectedConditions;
// Chai expect().to.exist syntax makes default jshint unhappy.
// jshint expr:true
module.exports = function() {
this.Given(/^thanx for help$/, function(next) {
browser.ignoreSynchronization=true;
browser.get('toto.com');
page1.login.sendKeys('login')
page1.password.sendKeys('P#ssword')
page1.validateButton.click();
browser.ignoreSynchronization=false;
page2.searchLink.click();
browser.waitForAngular();
browser.sleep(5000);
// console.log(dbQueryContract.numabo.result.rows[0][0]);
dbQueryContract().then(function(criteria) {
page2.searchInput.sendKeys(criteria, protractor.Key.ENTER);
});
next();
});
this.When(/^i learn more$/, function(next) {
browser.sleep(5000);
next();
});
};

This code doesn't look right. Your module is exporting the result of invoking new dbQueryContract();. However, that function isn't really a constructor function. You're not setting any properties on this or adding any properties to the prototype. Also, you're not returning anything, which is probably why your getting undefined later.
The next problem is that you're chaining a then call, assuming that the driver uses promises - it doesn't (at least not yet). You'd need to have your function return a deferred which you'd need to resolve at the right time yourself. There's some discussion here about adding more robust JavaScript layer:
https://github.com/oracle/node-oracledb/pull/321
If we go that route I'd like to see all async methods return a promise by default which would make things easier...

Related

NodeJS + MongoJS: Nested Callbacks Issue

I'm still a n00b with NodeJS so this question may be somewhat elementary.
I'm using MongoJS to read two logically related collections. The first find() returns a value that I pass to the second find() to get the information I need.
I've tried several strategies, the last one (snippet #1) being a class that I export.
Before that I just had a function that did a return, returning the desired value, i.e., "config[0]".
In this code all I did was set the "sapConfig" attribute to the word "test", but when I execute this code the value of "sapConfig" is always "null" after I call the "get_config()" method and - strangest of all - the reference to "this.sapConfig = 'test'" generates an error, i.e., "Cannot set property 'sapConfig' of undefined".
When I had the code just as a simple function with a return statement (snippet #2), no errors were generated but the value returned is always "undefined" although the console.log() statements show that the value of the variable being returned has the desired value. What gives?
Code Snippet #1: Returns Object
"use strict";
var mongojs = require('mongojs'); // MongoDB API wrapper
module.exports = function(regKey) {
this.regKey = regKey;
this.sapConfig = null;
this.get_config = function() {
// Read SAP connection information from our MONGO db
var db = mongojs('mongodb://localhost/MIM', ['Configurations','Registrations']);
db.Registrations.find({ key: this.regKey }, function(err1, registration){
console.log('Reg.find()');
console.log(registration[0]);
db.Configurations.find({ type: registration[0].type }, function(err2, config){
console.log('Config.find()');
console.log('config=' + config[0].user);
this.sapConfig = 'test';
});
});
}
this.get_result = function() {
return this.sapConfig;
}
}
Again, the code in snippet #1, when I make a call to "get_config()", results in an error when it executes the line "this.sapConfig = 'test'".
However, after this error I can execute "obj.get_result()" and I get the value to which it was initialized, i.e., null. In other words, that same code doesn't generate an error saying that the "this" is undefined as .in the "get_config()" method
Code Snippet #2: Using the "return" statement
"use strict";
var mongojs = require('mongojs'); // MongoDB API wrapper
module.exports = function(regKey) {
// Read SAP connection information from our MONGO db
var db = mongojs('mongodb://localhost/MIM', ['Configurations','Registrations']);
db.Registrations.find({ key: regKey }, function(err1, registration){
console.log('Reg.find()');
console.log(registration[0]);
db.Configurations.find({ type: registration[0].type }, function(err2, config){
console.log('Config.find()');
console.log('config=' + config[0].user);
return config[0].user;
});
});
}
When I receive the return value and inspect it, it's "undefined". For example, at the Node CL I issue the following commands:
var config = require('./config') // The name of the module above
> var k = config('2eac44bc-232d-4667-bd24-18e71879f18c')
undefined <-- this is from MongoJS; it's fine
> Reg.find() <-- debug statement in my function
{ _id: 589e2bf64b0e89f233da8fbb,
key: '2eac44bc-232d-4667-bd24-18e71879f18c',
type: 'TEST' }
Config.find()
config=MST0025
> k <-- this should have the value of "config[0]"
undefined
You can see that the queries were successful but the value of "k" is "undefined". What's going on here?
I don't care which approach I use I just need one of them to work.
Thanks in advance!
this.sapConfig is not accessible. Thats because this refers to within the current function. What I like todo, is have a variable that refers to the function instance that you know sapConfig is located in.
Ex:
function Foo() {
var self = this;
this.test = "I am test";
var bar = function(){
return function(){
console.log(this.test); //outputs undefined (because this refers to the current function scope)
console.log(self.test); //outputs "I am test";
}
}
}
Here is your first code snippit with my example implemented:
"use strict";
var mongojs = require('mongojs'); // MongoDB API wrapper
module.exports = function(regKey) {
var self = this;
this.regKey = regKey;
this.sapConfig = null;
this.get_config = function() {
// Read SAP connection information from our MONGO db
var db = mongojs('mongodb://localhost/MIM', ['Configurations', 'Registrations']);
db.Registrations.find({ key: this.regKey }, function(err1, registration) {
console.log('Reg.find()');
console.log(registration[0]);
db.Configurations.find({ type: registration[0].type }, function(err2, config) {
console.log('Config.find()');
console.log('config=' + config[0].user);
self.sapConfig = 'test';
});
});
}
this.get_result = function() {
return self.sapConfig;
}
}
For your second snippet. You are trying to return a value from within your nested callback. Since nested functions are asyncronous, you cannot do that.
Here is how I like to return values from nested callbacks:
Ex2:
//Function example
var functionWithNested = function(done) {
//Notice the done param.
// It is going to be a function that takes the finished data once all our nested functions are done.
function() {
//Do things
function() {
//do more things
done("resultHere"); //finished. pass back the result.
}();//end of 2nd nested function
}(); //end of 1st nested function
};
//Calling the function
functionWithNested(function(result) {
//Callback
console.log(result); //resultHere
})
Here is your code using that example:
"use strict";
var mongojs = require('mongojs'); // MongoDB API wrapper
module.exports = function(regKey, done) {
// Read SAP connection information from our MONGO db
var db = mongojs('mongodb://localhost/MIM', ['Configurations', 'Registrations']);
db.Registrations.find({ key: regKey }, function(err1, registration) {
console.log('Reg.find()');
console.log(registration[0]);
db.Configurations.find({ type: registration[0].type }, function(err2, config) {
console.log('Config.find()');
console.log('config=' + config[0].user);
done(config[0].user);
});
});
}
//Then wherever you call the above function use this format
// if config is the name of the function export above...
new Config().(regKey, function(result){
console.log(result); //config[0].user value
})
Lots and lots of code, but I hope you were able to follow it. Let me know if you have any more questions! Cheers.

Storing a MongoDb result in a variable to run a Mocha test, getting undefined

So, I'm trying to run a Mocha test, more precisely a Chakram test.
The problem is, I'm getting data from a collection in MongoDB and I want to store that data in a global variable to run some tests.
The problem is that inside the call back I get the data, but it doesn't set the global variables to run the tests.
Here is the code
var chakram = require('chakram'),
expect = chakram.expect;
describe("Test", function() {
var gl_email;
var gl_token;
before("Getting user data", function() {
var setAccessData = function() {
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect('mongodb://localhost/virtusapp', function(err, db) {
if (err) throw err;
console.log("Connected to Database");
var user = db.collection('users').findOne({
name: "virtus-app"
});
user.then(function(result) {
email = result.email;
token = result.token1 + result.token2;
db.close(test(email, token))
});
});
}
var test = function(email, token) {
gl_email = email;
gl_token = token;
//Here the email and token are set, but it doesnt set the global variables
}
setAccessData();
});
it("should have set global email variable", function() {
//here gl_email should be set, but I get UNDEFINED.
expect(gl_email).to.eql("virtus-app#virtus.ufcg.edu.br");
})
});
I believe that the problem is not with Chakram, because I haven't used in this code yet.
Your before function is asynchronous. You should use a different signature to tell mocha that it has to wait until it's finished before running the tests.
before("Getting user data", function(done) {
...
var test = function(email, token) {
gl_email = email;
gl_token = token;
done();
}
...
});
Only after done() is called the rest of the code is going to be executed by mocha.
Mocha docs has a very comprehensive guide on how to test asynchronous code https://mochajs.org/#asynchronous-code

How can I test through a mocked promise with Mocha?

My node.js code is:
function getPatientNotificationNumbers(patientId) {
patientId = patientId && patientId.toString();
var sql = "SELECT * FROM [notification_phone_number] ";
sql += "WHERE patient_id = " + escapeSql(patientId);
return sqlServer.query(sql).then(function(results) {
var phoneNumbers = _.map(results[0], function (result) {
var obj = {
id: result.id,
phoneNumber: result.phone_number,
isPrimary: result.is_primary,
isVerified: result.is_verified,
patientId: result.patient_id
}
return obj;
});
return phoneNumbers;
});
}
Pretty simple and straight forward. What I want to test is that the return of this function, properly resolved, is an array of phoneNumbers that match that format.
sqlServer is require'd above and I have a ton of things require'd in this file. To stub them out, I am using mockery, which seems to be pretty great.
Here is my test, so far:
before(function() {
deferred = Q.defer();
mockery.enable();
moduleConfig.init();
mockery.registerSubstitute('moment', moment);
mockery.registerAllowable('../../util');
mockStubs.sqlServer = {
query: sinon.stub().returns(deferred.promise)
}
mockery.registerMock('../../db/sqlserver', mockStubs.sqlServer);
methods = require('../../../rpc/patient/methods');
});
beforeEach(function() {
deferred = Q.defer();
})
it('should get the patient notification numbers', function(done) {
// sinon.spy(sqlServer, 'query').and.returnValue(deferred.promise);
deferred.resolve('here we go');
methods.getPatientNotificationNumbers(1).then(function(result) {
console.log(result);
done();
});
});
However, it never gets past sqlServer.query in my code. So the results are pointless. I also tried something like:
response = methods.getPatientNotificationNumbers(1)
but when I console.log(response), it's basically {state: 'pending'}, which I guess is an unresolved promise.
So I'm all over the place and I'm open to using whatever libraries make things easy. I am not married to mockery, sinon or whatever else. Any suggestions would help.
Thanks!
So another approach is to use a combination of rewire and deride.
var should = require('should-promised');
var rewire = require('rewire');
var Somefile = rewire('./path/to/file');
var deride = require('deride');
var sut, mockSql;
before(function() {
mockSql = deride.stub(['query']);
Somefile.__set__('sqlServer', mockSql);
sut = new Somefile();
});
describe('getting patient notification numbers', function() {
beforeEach(function() {
mockSql.setup.query.toResolveWith(fakeSqlResponse);
});
it('resolves the promise', function() {
sut.getPatientNotificationNumbers(id).should.be.fulfilledWith(expectedPhoneNumbers);
});
it('queries Sql', function(done) {
sut.getPatientNotificationNumbers(id).then(function() {
mockSql.expect.query.called.once();
done();
});
});
});
This will mean that you do not need to change your production code and you can easily start testing the un-happy paths using something like this:
it('handles Sql errors', function(done) {
mockSql.setup.query.toRejectWith(new Error('Booom'));
sut.getPatientNotificationNumbers(id)
.then(function(){
done('should not have resolved');
})
.catch(function(e) {
e.should.be.an.Error;
e.message.should.eql('Booom');
done();
});
});
Or even more succinctly:
it('handles Sql errors', function(done) {
mockSql.setup.query.toRejectWith(new Error('Booom'));
sut.getPatientNotificationNumbers(id).should.be.rejectedWith(/Booom/);
});
I would zoom out a little bit and think of the test strategy. Assume that the goal is to test your model layer (methods) on top of the sql server. It can be done without stubbing out sql server. Your test suite layer can have set of util methods to create, initialize and drop the db which can be called from before, beforeEach etc.
Pros of doing this:
Testing real product code path is better (than stubbed code path).
Stubbing is better if you suspect the underlying layer bugs to generate noise. sqlserver layer is likely stable.
model layer seem to be simple enough that does not require testing in isolation.
Stubbing would make sense if you are trying to test sqlserver failure handling in your model layer. Then the stub layer can fake such errors - to exercise error paths in model code.
This is based on limited view of your problem. If there is more to it. Pl do share and we can take it from there.
It seems to me your intent is to test the function your passing the the .then() method, not the sqlserver library nor the actual promise it returns. We can assume those have already been tested.
If this is the case, you can simply factor out that function (the one that extracts the phone numbers from the SQL result) and test it independently.
Your code would become something along the lines of:
var getNumbers = require('./getNumbers');
function getPatientNotificationNumbers(patientId) {
patientId = patientId && patientId.toString();
var sql = "SELECT * FROM [notification_phone_number] ";
sql += "WHERE patient_id = " + escapeSql(patientId);
return sqlServer.query(sql).then(getNumbers);
}
... and your ./getNumbers.js file would look something like:
function getNumbers(results) {
var phoneNumbers = _.map(results[0], function (result) {
var obj = {
id: result.id,
phoneNumber: result.phone_number,
isPrimary: result.is_primary,
isVerified: result.is_verified,
patientId: result.patient_id
}
return obj;
});
return phoneNumbers;
}
module.exports = getNumbers;
Now you can test it independently:
var getNumbers = require('../path/to/getNumbers');
...
it('should get the patient notification numbers', function(done) {
var sampleResults = [ ... ]; // sample results from sqlServer
var phoneNumbers = getNumbers(sampleResults);
assert(...); // make what ever assertions you want
});

Unit testing node.js model code accessing mongodb, without actually accessing the database

For example, I have the code below:
var db = require('./_mongo.js');
module.exports = {
check: function (cb) {
var content = {};
content.collection = 'counters';
content.query = {_id: 'ping'};
content.columns = {};
db.read(content, function(err, result){
if (err) {
cb(-1);
}
else {
cb(0);
}
});
}
};
How do I write a unit test for the 'check' function, without actually accessing the database, while at the same time checking if I am able to code the correct 'content' variable being passed to the read method?
You can mock an entire module with a mock framework, like sinon.js:
var db = sinon.mock(require('_mongo.js'))
I would not recommend to mock database access, it could require you to code all possible responses...
It would be best if you would hide the database access behind an abstracted service layer and mock that layer.
For example, you can create a database access layer in this way:
var db = require('./_mongo.js');
module.exports = {
//this is a mockable method
getCounter: function (id, callback) {
var content = {};
content.collection = 'counters';
content.query = {_id: id};
content.columns = {};
db.read(content, callback);
}
};
//and then using it
module.exports = {
check: function (cb) {
//access the actual method or the mock
da.getCounter('ping', function(err, result){
if (err) {
cb(-1);
}
else {
cb(0);
}
});
}
};
test-studio provides mechanisms for stubbing out module dependencies. It also supports things like executing individual or groups of tests and stepping node-inspector into individual tests.
Read more about it here.

Making Mocha test work for my db module

I am new to Mocha, and only a little experience with Node/Express. My DbProvider module works perfectly (mongodb) when I am access it through my Express app. And now I want to test it. I have read the Mocha site and some tutorials I could find. But I have big trouble of finding an real-world example out there that I could follow (any links much appreciated!).
Here is my unsuccessful attempt to write a testfile:
var DbProvider = require('../db').DbProvider;
var assert = require('assert');
var dbProvider = new DbProvider('localhost', 27017, 'mydb');
var util = require('util');
console.log(util.inspect(dbProvider));
describe('DbProvider', function(){
describe('findAllNotes', function(){
it('should return some notes', function(){
dbProvider.findAllNotes({}, function (err, result){
assert(result.length > 0);
});
})
})
})
The output I get is this:
$ mocha
{}
✖ 1 of 1 test failed:
1) DbProvider findAllNotes should return some notes:
TypeError: Cannot call method 'collection' of undefined
at DbProvider.doOperation (/Users/frode/Node/json/db.js:46:11)
at DbProvider.findAllNotes (/Users/frode/Node/json/db.js:56:8)
at Context.<anonymous> (/Users/frode/Node/json/test/test.js:15:18)
(cutting out the rest)
It seems that I am unsuccessful to create the dbProvider. This works perfectly in my app... How can I make this work? (And perhaps also: Is the way I have set it up in general ok?)
Edit: Here is the db.js file:
// Database related
'use strict';
var MongoClient = require('mongodb').MongoClient;
var BSON = require('mongodb').BSONPure;
var ObjectID = require('mongodb').ObjectID;
var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
var Validator = require('validator').Validator
var fieldMaxLength = 1024;
//var util = require('util');
var DbProvider = function(host, port, database) {
var dbUrl = "mongodb://"+host+":"+port+"/"+database;
var self = this;
MongoClient.connect(dbUrl, function(err, db) {
self.db = db;
});
};
// Do some basic validation on the data we get from the client/user
var validateParams = function(params, callback) {
// Let´ do a quick general sanity check on the length on all fields
for(var key in params) {
if(params[key].length > fieldMaxLength) callback(new Error('Field ' + key + ' is too long.'));
}
// and the let us check some specific fields better
if (params._id) {
if(checkForHexRegExp.test(params._id)) {
// In case of '_id' we also need to convert it to BSON so that mongodb can use it.
params._id = new BSON.ObjectID(params._id);
} else {
var err = {error: 'Wrong ID format'};
}
}
if(err) callback(err);
}
// Generalized function to operations on the database
// Todo: Generalize even more when user authenication is implemented
DbProvider.prototype.doOperation = function(collection, operation, params, callback) {
validateParams(params, callback);
var operationCallback = function(err, result) {
callback(err, result);
};
this.db.collection(collection, function(err, collection) {
if(operation==='find') {
collection.find().toArray(operationCallback);
} else {
collection[operation](params, operationCallback);
}
});
}
DbProvider.prototype.findAllNotes = function(params, callback) {
this.doOperation('notes', 'find', params, callback);
};
DbProvider.prototype.findNoteById = function(params, callback) {
this.doOperation('notes', 'findOne', params, callback);
};
DbProvider.prototype.saveNote = function(params, callback) {
params.created_at = new Date();
this.doOperation('notes', 'save', params, callback);
};
DbProvider.prototype.deleteNote = function(params, callback) {
this.doOperation('notes', 'remove', params, callback);
};
DbProvider.prototype.findUser = function(params, callback) {
this.doOperation('users', 'findOne', params, callback);
};
exports.DbProvider = DbProvider;
SOLUTION:
After Benjamin told me to handle the async nature of mongodb connecting to the database, and inspired by his suggestion on how to adapt the code, I split the constructor function DbProvider into two parts. The first part, the constructor DbProvider now just saves the db-parameters into a variable. The second part, a new function, DbProvider.connect does the actual async connection. See below.
var DbProvider = function(host, port, database) {
this.dbUrl = "mongodb://"+host+":"+port+"/"+database;
};
DbProvider.prototype.connect = function(callback) {
var self = this;
MongoClient.connect(this.dbUrl, function(err, db) {
self.db = db;
callback();
});
};
So I can now make a Mocha test like this (and async tests also need the "Done" included, like you see in the code below):
var assert = require('assert');
var DbProvider = require('../db').DbProvider;
var dbProvider = new DbProvider('localhost', 27017, 'nki');
describe('DbProvider', function(){
describe('findAllNotes', function(){
it('should return some notes', function(done){
dbProvider.connect(function(){
dbProvider.findAllNotes({}, function (err, result){
assert(result.length > 0);
done();
});
});
})
})
})
Note that the acutal test ("should return some notes") is nothing to be proud of. What I wanted here was to get set up so I am able to test something. Now that I finally acutally can do that, I need to write good tests (something in the the line of having a test database, clear it, test insert a document, test search for a document, and so on...).
And in my Express app, I used to set up the database like this:
var DbProvider = require('./db').DbProvider;
// Setup db instance
var dbProvider = new DbProvider(
process.env.mongo_host || 'localhost',
process.env.mongo_port || 27017,
process.env.mongo_db || 'nki'
);
Now I do the same, but in addition, I call the new connect-function:
// Connect to db. I use (for now) 1 connection for the lifetime of this app.
// And I do not use a callback when connecting here (we do in the testing)
dbProvider.connect(function(){});
Benjamin actually pointed out that it may be ok but not the best practice to have the database set up like this in an Express app. But until I figure out what the best practice really is, I will leave this code as it is. Here is a couple of links reagarding the subject I found (but I have still not concluded of how I will solve it myself):
What's the best practice for MongoDB connections on Node.js? and
[node-mongodb-native] MongoDB Best practices for beginner
If you like, you are very welcome to follow/fork/whatever this project on github. My goal is to get it as production ready I can. The link is
https://github.com/frodefi/node-mongodb-json-server
MongoClient.connect is asynchronous.
From the docs:
callback (function) – this will be called after executing this method. The first parameter will contain the Error object if an error occured, or null otherwise. While the second parameter will contain the initialized db object or null if an error occured.
That means DbProvider.db isn't set yet in the test which is why you're getting undefined.
In here:
MongoClient.connect(dbUrl, function(err, db) {
self.db = db;
});
You're telling it "update self.db after the connection happened", which is at least one event loop tick after this one (but may be more). In your mocha code you're executing your .describe and .it methods right after creating your DbProvider instance which means it was not initialized yet.
I suggest that you re-factor DbProvider to return a callback instead of being a constructor function. Maybe something along the lines of:
var getDbProvider = function(host, port, database,callback) {
var dbUrl = "mongodb://"+host+":"+port+"/"+database;
MongoClient.connect(dbUrl, function(err, db) {
self.db = db;
callback(db);
});
};
Which also means moving all the DBProvider methods to an object (maybe the callback will return a dbprovider object and not just a db?).
Another bug solved by using Unit Tests :)
This is what I used: https://github.com/arunoda/mocha-mongo
It has set of testing helpers for mongodb

Resources