I am a complete beginner with Node.JS and Mocha and was tasked to write unit test's for a group project. My problem is that i do not even know where to start since the return value is a promise.
Watching a lot of guides i have learned how to check return values for common functions but it wouldn't help me with a real world example.
If any expierienced developer could help me with a guide and code example specific to the function i listed i could hack and understand it and apply to other functions as well.
Here is a code example that get's statistics from a CSV File
function getStatistics() {
return new Promise((resolve, reject)=>{
try {
let readStatistics = [];
const statisticsReadStream = fs.createReadStream(statisticsFileName);
csv.fromStream(statisticsReadStream, {headers: true})
.transform(function (data) {
data.avgDuration = parseFloat(data.avgDuration);
data.avgPassed = parseFloat(data.avgPassed);
data.avgReachedPoints = parseFloat(data.avgReachedPoints);
data.minReachedPoints = parseInt(data.minReachedPoints);
data.maxReachedPoints = parseInt(data.maxReachedPoints);
return data;
})
.on("data", function (data) {
readStatistics.push(data);
})
.on("end", function () {
resolve(readStatistics);
statisticsReadStream.close();
});
}catch(err){
reject();
}
});
}
In mocha, you can return the promise from the test function to indicate that the test (it) has completed.
describe('My Test', function () {
it('should do something cool', function () {
return getStatistics().then(readStatistics => {
// Assert whatever you want here
});
});
});
Any error that gets thrown from your getStatistics function or any assertion error will cause the test to fail.
If you are specifically looking to see if something throws an error, you can catch the error (reject()) in a test too.
describe('My Test', function () {
it('should do something cool', function () {
return getStatistics().catch(error => {
// Assert whatever you want here about the error
});
});
});
https://mochajs.org/#asynchronous-code
Here is the code:
`describe('Statistic', function(){
it('should transform data into strings', function () {
return statGet().then(readStatistics => {
let maybe = statGet();
var csv = maybe.csv;
let dat = function (data) {
data.avgDuration = "1,2";
data.avgPassed = "2,3";
data.avgReachedPoints ="3,4";
data.minReachedPoints = "4";
data.maxReachedPoints = "5";
return data;
}
assert.typeOf(csv.transform(dat, 'string'));
});
});
});`
on the other hand, i have liuttle idea of what i shold be testing in the first place.
I feel hopelessly lost. I want to go back to hello world =(
Related
I am using cypress framework to test node app. I want to check variable is set to 'SUCCESS'. Intially it is set to 'FAILURE' but from cypress exec it is set to 'SUCCESS'. But when I invoke the function it is still 'FAILURE'. Can someone help me understand where I made it wrong.
cypress\support\returnStatus.js
module.exports = function() {
this.returnStatus = "FAILURE";
this.SuccessMsg = function() {
cy.exec('echo hello', { log: true, failOnNonZeroExit: false }).then((output) => {
this.returnValue = "SUCCESS";
});
}
}
cypress\integration\checkReturnValue.spec.js
let Status = require('../support/returnStatus');
let helper = new Status();
describe("Check return value", function(){
it("should check latest value", function(){
let reply = helper.SuccessMsg();
console.log(reply);//still it prints /*FAILURE*/
})
});
There's some async stuff going on here, since cy.exec spawns a child process.
Normally you would await the async call, but Cypress commands only return chainers (they don't really follow the promise pattern), so you will need to add a Promise into the mix and await it.
Helper
module.exports = function() {
this.returnStatus = "FAILURE";
this.SuccessMsg = function() {
return new Cypress.Promise(resolve => {
cy.exec('echo hello', { log: true, failOnNonZeroExit: false }).then((output) => {
this.returnValue = "SUCCESS";
resolve(this.returnValue)
});
})
}
}
Test
let Status = require('../support/returnStatus');
let helper = new Status();
describe("Check return value", function(){
it("should check latest value", async function(){ // NOTE async test
let reply = await helper.SuccessMsg();
console.log('reply', reply); // now it prints /*SUCCESS*/
})
});
If you turned your helper function into a custom command, likely the test will not need to be made async since Cypress automatically waits for promises to resolve.
Say I have more than 100 requests in my application. It would be cumbersome and time consuming to write as many success and error functions to handle response.
At the moment I do like this:
angular
.module('mahanApp')
.service('HttpService', function($http) {
// Get all name
function getNames() {
return $http.get('/data/names/')
}
return {
getNames: getNames
}
.controller('NameController', function NameController(
HttpService) {
var ctrl = this;
// Fetch all templates name;
HttpService.getNames().then(
function success(response) {
ctrl.names = response.data;
},
function error(response) {});
})
});
As you see, it is a lot of work to write so many callback functions for 100+ requests. I wonder if it is possible to avoid doing so.
There is this questions that suggests to do like the code below.
angular
.module('mahanApp')
.service('HttpService', function($http) {
var success = function(response) {
return response.data;
},
error = function(error) {
return error.data;
};
// Get all name
function getNames() {
return $http.get('/data/names/').then(success, error)
}
return {
getNames: getNames
}
.controller('NameController', function NameController(
HttpService) {
var ctrl = this;
})
});
In the NameController, I used ctrl.names = HttpService.getNames(). But ctrl.names is undefined. How to use the HttpService.getNames() in NameController?
Update:
A few people has commented to resolve the promise.
HttpService.getNames().then(function success(result) {
ctrl.names = result;
})
It works. However, I still write 100+ functions to resolve the promise.
How to avoid repeating the success and error functions or if possible the whole then() method?
I will try to sum up the discussion and some ways you can remove duplication:
First of all, your second approach is the right direction to re-using a function in your controllers.
A classical approach would be this:
return {
getNames: getNames
}
.controller('NameController', function NameController(
HttpService) {
var ctrl = this;
HttpService.getNames().then(function(res) { ctrl.names = res; } );
})
Note that you can even extract this more if all your calls are following the same format, for example you could:
// utility function that loads something into your controller
var load = function(request, ctrl, prop) {
request.then(function(res) { ctrl[prop] = res; }
};
// this way you can have many of requests with minimal code
load(HttpService.getNames, ctrl, 'names');
load(HttpService.getBooks, ctrl, 'books');
load(HttpService.getThings, ctrl, 'things');
...
Alternatively, if you can use ES2017 in your project (e.g. your code is transpiled) you can simplify using some of the new features without the need of an additional function:
// Using promise all will assure these are all fetched asynchronously in respect to one another
Promise.all([
async () => ctrl.names = await HttpService.getNames(),
async () => ctrl.books = await HttpService.getBooks(),
async () => ctrl.things = await HttpService.getThings()
]);
Finally, you might actually want to parameterize your request so that you have just one:
// Get all
function getAll(what) {
return $http.get('/data/' + what).then(success, error)
}
Then you don't need to have a different method for each type, you'll just do:
getAll('names');
getAll('books');
...
I have a SignalR function that retrieves some data from a hub.
Example:
var getApple = function () {
hub.server.getFruits("Apple").done(function (result) {
return result;
});
};
I want to be able to return the value of getFruits() to getApple() when getFruits() is done;
console.log(getApple());
However using this approach, getApple() will always be undefined.
I've tried the following (really ugly) solution
var getApple = function () {
var result2;
hub.server.getFruits("Apple").done(function (result) {
result2 = result;
});
return result2;
};
However getApple() was undefined until getFruits() was completed. I was able to workaround this with a setTimeout() but this is obviously a terrible solution.
I've also tried the following but this does not seem the return the actual result of getFruits().
var getApple = function () {
return hub.server.getFruits("Apple").done(function (result) {
return result;
});
};
From my understanding I'm lacking async javascript/jquery promise(?) knowledge. I've read the documentation and have looked at some other SO questions but was unable to properly place it into context.
I would greatly appreciate a code example of a proper implementation for this.
Something like this will work - basically you need to create a promise object in a function like so:
var callTestMethod = function() {
var promise = new Promise(function(resolve, reject) {
var result = myHub.server.testMethod();
resolve(result);
});
return promise;
}
and this is how you call it and wait for the result:
$.when(callTestMethod()).done(function(result) {
alert(result);
})
I am using npm 'powershell' package for executing PowerShell commands and reading related output. I want to write a function that would return standard command output (so that I could call the the function and use its return value in assertions etc.).
const PowerShell = require("powershell");
var myFunction = function (command) {
let ps = new PowerShell(command);
ps.on("error", err => {
console.error(err);
});
ps.on("output", data => {
console.log(data);
//return data; <-- this does not work
});
ps.on("error-output", data => {
console.error(data);
});
ps.on("end", code => {
console.log("The end");
});
};
I want myFunction to return data value (from standard output). However, I don't know how to do it properly. Could you please advise?
Look into how callbacks work. An example for your function would be
var myFunction = function (command, callback) {
// code
ps.on("output", data => {
callback(data)
});
// code
};
myFunction('ls', function (data) {
console.log('The callback data:', data);
});
I've been using Bluebird a lot recently on a HAPI API development. I've just run into my first real problem, that perhaps my understanding or naivety has me stumped.
The following code is an example of what I am facing:-
var Promise = require('bluebird'),
stuff = require('../stuff');
module.exports = {
getSomething: function(request, reply) {
var p = Promise.resolve();
p = p.then(function() {
return db.find() //etc. etc.
});
p = p.then(function(resultFromPromise) {
//problems begin here
var data = stuff.doSomeReallyLongAndBoringFunction(resultFromPromise);
return data;
});
p.then(function(data) {
//no data here.
});
};
};
I've commented where the problems usually begin. the stuff.doSomeReallyLongAndBoringFunction() returns an object (using more promises concidently) and it's this object I want to access, but //no data here always fires before data returns. stuff.doSomeReallyLongAndBoringFunction() continues to run regardless and completes successfully, but after the code goes async, I don't know how to promise that function's return value back.
Can anyone offer any guidance? Please accept my apologies for any naivety in the question!
Help as always, is appreciated
NB just for clarity, stuff.doSomeReallyLongAndBoringFunction() does not return a Promise itself. Although, I did try return new Promise(reject, resolve) { }); manual wrap. It is simply a function that uses promises itself (successfully) to get data.
Update 1
stuff.doSomeReallyLongAndBoringFunction() is too big to post directly, but it does something like this:-
var Promise = require('bluebird'),
rp = require('request-promise');
module.exports = {
doSomeReallyLongAndBoringFunction: function() {
var p = Promise.resolve();
p = p.then(function() {
return db.find() //etc. etc.
});
p.then(function() {
rp(options).then(function(response){
//get some data from remote location
}).then(function(dataFromService) {
//do some jiggery pokery with said data
var marshalledData = dataFromService;
db.something.create({
Field: 'something'
}).exec(function(err, saved) {
return marshalledData;
});
});
}).catch(function(err) {
});
};
};
Update 2
Thank you Justin for your help. Here is the actual code, perhaps this may help?
Promise.resolve()
.then(function() {
if(typeof utils.intTryParse(place) !== 'number') {
return foursquare.createPlaceFromFoursquare(sso, place, request, reply);
} else {
return { Place: { PlaceId: place }};
}
}).then(function(placeObj) {
console.log('Place set as', placeObj); //always returns undefined, despite function actually completing after async op...
});
If your doSomeReallyLongAndBoringFunction is really running asynchronously, then it doesn't make sense to run it the way you have setup.
Edit - Here's a simple explanation of the way your code looks to be running vs a refactored version. It's been simplified , so you'll need to fill in the relevant sections with your actual implementation.
var Promise = require('bluebird');
function myAsync() {
setTimeout(function(){
return 'done sleeping';
}, 2000);
};
//The way your code is running
Promise.resolve()
.then(function(){
return 'hello';
})
.then(function(done){
console.log(done);
return myAsync(); //your error is here
})
.then(function(done){
console.log(done);
});
//refactored
Promise.resolve()
.then(function(){
return 'hello';
})
.then(function(done){
console.log(done);
return new Promise(function(resolve) {
setTimeout(function(){
resolve('done sleeping');
}, 2000);
});
})
.then(function(done){
console.log(done);
});
just for clarity, stuff.doSomeReallyLongAndBoringFunction() does not return a Promise itself.
And that's your problem. As it does something asynchronous and you want to get its result, it should return a promise. In fact, that's the case for every asynchronous function, especially then callbacks! It should be something like
module.exports = {
doSomeReallyLongAndBoringFunction: function() {
return db.find()
// ^^^^^^
.then(function() {
return rp(options).then(function(response){
// ^^^^^^
//get some data from remote location
}).then(function(dataFromService) {
//do some jiggery pokery with said data
var marshalledData = dataFromService;
return db.something.create({
// ^^^^^^
Field: 'something'
}).execAsyc();
});
}).catch(function(err) {
});
}
};
Your getSomething method has the same issues, and should look like this:
var createPlace = Promise.promisify(foursquare.createPlaceFromFoursquare);
module.exports = {
getSomething: function(request) {
var p;
if (typeof utils.intTryParse(place) !== 'number')
p = createPlace(sso, place, request); // this returns a promise!
else
p = Promise.resolve({Place: {PlaceId: place}});
return p.then(function(placeObj) {
// ^^^^^^
console.log('Place set as', placeObj);
});
}
};
See also these generic rules for promise development.
doSomeReallyLongAndBoringFunction needs to look like this:
doSomeReallyLongAndBoringFunction: function(param) {
var resolver = Promise.defer();
/*
* do some asynchronous task and when you are finished
* in the callback, do this:
*/
resolver.resolve(resultFromAsyncTask);
/*
*
*
*/
return resolver.promise;
}