How to increase the code coverage using istanbul in node.js - javascript

I am using Istanbul for code coverage, but i m getting very low coverage percentage particularly in Models file.
Consider the following is the model file:
ModelA.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
var app = require('../server')
var db = require('../db/dbConnection');
var config = require('../configs/config')
const Schema1 = new Schema({ 'configurations': [] });
exports.save = function (aa, data, callback) {
var logMeta = {
file: 'models/modelA',
function: 'save',
data: {},
error: {}
}
if (!aa) {
return callback('aa is required')
}
global.logs[aa].log('info', 'AA: ' + aa, logMeta);
db.connectDatabase(aa, function(error, mongoDB){
if(error){
logMeta.data['error'] = error
global.logs[aa].log('error', 'error', logMeta);
return callback(error)
}
const ModelA = mongoDB.model('bbb', cccc);
ModelA.findOneAndUpdate({}, data, {upsert: true, new: true, runValidators: true}, function(error ,result){
if (error) {
logMeta.data['error'] = error
global.logs[aa].log('error', 'error', logMeta);
}
else {
logMeta.data = {}
logMeta.data['result'] = JSON.parse(JSON.stringify(result))
global.logs[aa].log('info', 'result', logMeta);
}
callback(error, result);
});
})
}
TestA.js:
var should = require('should'),
sinon = require('sinon'),
ModelA= require("../models/ModelA");
describe('Model test', function () {
it('Should save Model', function (done) {
var todoMock = sinon.mock(new ModelA({'configurations': []}));
var todo = todoMock.object;
todoMock
.expects('save')
.yields(null, 'SAVED');
todo.save(function(err, result) {
todoMock.verify();
todoMock.restore();
should.equal('SAVED', result, "Test fails due to unexpected result")
done();
});
});
});
But i am getting codecoverage percentage 20. SO how can i increase the percentage:
ALso:
1.Whether i have to mock the db.connectDatabase if yews how can i acheive that?
Whether i have to use TestDb to run all my UnitTest? Or i have to assert??
Code Coverage will work for Unit Test or integration test???
Please share your ideas. Thanks

I have been using Istanbul to 100% code cover most of my client/server projects so I might have the answers you are looking for.
How does it work
Whenever you require some local file, this gets wrapped all over the place to understand if every of its parts is reached by your code.
Not only the required file is tainted, your running test is too.
However, while it's easy to code cover the running test file, mocked classes and their code might never be executed.
todoMock.expects('save')
Accordingly to Sinon documentation:
Overrides todo. save with a mock function and returns it.
If Istanbul tainted the real save method, anything within that scope won't ever be reached so that you are actually testing that mock works, not that your real code does.
This should answer your question: Code Coverage will work for Unit Test or integration test ???
The answer is that it covers the code, which is the only thing you're interested from a code cover perspective. Covering Sinon JS is nobody goal.
No need to assert ... but
Once you've understood how Istanbul works, it follows up naturally to understand that it doesn't matter if you assert or not, all it matters is that you reach the code for real and execute it.
Asserting is just your guard against failures, not a mechanism interesting per se in any Istanbul test. When your assertion fails, your test does too, so it's good for you to know that things didn't work and there's no need to keep testing the rest of the code (early failure, faster fixes).
Whether you have to mock the db.connectDatabase
Yes, at least for the code you posted. You can assign db as generic object mock to the global context and expect methods to be called but also you can simplify your life writing this:
function createDB(err1, err2) {
return {
connectDatabase(aa, callback) {
callback(err1, {
model(name, value) {
return {
findOneAndUpdate($0, $1, $3, fn) {
fn(err2, {any: 'object'});
}
};
}
});
}
};
}
global.db = createDB(null, null);
This code in your test file can be used to create a global db that behaves differently accordingly with the amount of errors you pass along, giving you the ability to run the same test file various times with different expectations.
How to run the same test more than once
Once your test is completed, delete require.cache[require.resolve('../test/file')] and then require('../test/file') again.
Do this as many times as you need.
When there are conditional features detection
I usually run the test various times deleting global constructors in case these are patched with a fallback. I also usually store them to be able to put 'em back later on.
When the code is obvious but shouldn't be reached
In case you have if (err) process.exit(1); you rarely want to reach that part of the code. There are various comments understood by Istanbul that would help you skip parts of the test like /* istanbul ignore if */ or ignore else, or even the generic ignore next.
Please consider thinking twice if it's just you being lazy, or that part can really, safely, be skipped ... I got bitten a couple of times with a badly handled error, which is a disaster since when it happens is when you need the most your code to keep running and/or giving you all the info you need.
What is being covered?
Maybe you know this already but the coverage/lcov-report/index.html file, that you can open right away with any browser, will show you all the parts that aren't covered by your tests.

Related

How to mock JSON file in Jest

I am using Jest to test my API and when I run my tests, my JSON file results.json gets written to due to the following line in my API app.js (which I don't want happening):
fs.writeFile('results.json', JSON.stringify(json), (err, result) => {
if (err) console.log('error', err);
});
This is what my Jest file looks like:
const request = require('supertest');
const app = require('./app');
// Nico Tejera at https://stackoverflow.com/questions/1714786/query-string-encoding-of-a-javascript-object
function serialise(obj){
return Object.keys(obj).map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`).join('&');
}
describe('Test /addtask', () => {
test('POST /addtask Successfully redirects if newDate and newTask filled in correctly', () => {
const params = {
newTask: 'Example',
newDate: '2020-03-11'
};
return request(app)
.post('/addtask')
.send(serialise(params))
.expect(301);
});
});
I tried creating a mock of the JSON file and placed it outside the describe statement to prevent the actual results.json file being written to:
jest.mock('./results.json', () => ({ name: 'preset1', JSONtask: [], JSONcomplete: [] }, { name: 'preset2', JSONtask: [], JSONcomplete: [] }));
But this doesn't change anything. Does anyone have any suggestions?
I have seen other solutions to similar problems but they don't provide the answer I'm looking for.
EDIT: Although not a very good method, one solution to my problem is to wrap the fs.writeFile within the statement
if (process.env.NODE_ENV !== 'test') {
//code
};
although this would mean that fs.writeFile cannot be tested upon.
NOTE: I am still accepting answers!
Your issue is that the code you want to test has a hard-coded I/O operation in it, which always makes things harder to test.
What you'll want to do is to isolate the dependency on fs.writeFile, for example into something like a ResultsWriter. That dependency can then be injected and mocked for your test purposes.
I wrote an extensive example on a very similar case with NestJS yesterday under how to unit test a class extending an abstract class reading environment variables, which you can hopefully adapt to your needs.
jest.mock(path, factory) is for mocking JS modules, not file content.
You should instead mock fs.writeFile and check that it has been called with the expected arguments. The docs explain how to do it.

Best way to test IIFE (Immediately Invoked Function Expression)

So I have an existing application which uses IIFEs extensively in the browser. I'm trying to introduce some unit testing into the code and keep with the pattern of IIFE for new updates to the code base. Except, I'm having trouble even writing a test which gives me a handle to the code. For example I see this type of logic all over the code base:
var Router = (function (router) {
router.routeUser = function(user) {
console.log("I'm in! --> " + user)
};
return router;
})(Router || {});
Then the JS file is included in a script tag in the markup:
<script src="js/RouteUser.js"></script>
and called like this in the production code:
Router.routeUser(myUser)
So my question is, how do I write a test which tests the method routeUser? I've tried this in my Mocha Test:
var router = require('../../main/resources/public/js/RouteUser');
suite('Route User Tests', function () {
test('Route The User', function () {
if (!router)
throw new Error("failed!");
else{
router.routeUser("Me")
}
});
});
But I get an exception:
TypeError: router.routeUser is not a function
at Context.<anonymous> (src\test\js\RouteUser.test.js:8:20)
Then I tried returning the method, which gives the same error:
var Router = (function (router) {
return {
routeUser: function (user) {
console.log("I'm in! --> " + user)
}
}
}
)(Router || {});
Can anyone point me the right direction here?
It sounds that...
you have a codebase of scripts that are only used in the browser context (usage of IIFE suggests this)
you'd like to introduce browserless unit tests (Jest, Mocha?) using node.js (good idea!)
but you probably don't want to migrate the whole codebase to a different coding style at this moment in time (can be a lot of work depending on the size of your codebase)
Given these assumptions, the problem is that you want your code to...
act as a script when used on production (set global window.Router etc)
act as a module when used in unit tests so that you can require() it in unit tests
UMD
UMD, or universal module definition, used to be a common way to write code so that it can work in multiple environments. Interesting approach, but very cumbersome, and I like to think UMD is a thing of the past nowadays...
I'll leave it here for completeness.
Just take UMD's idea
If the only thing you want for now to make a specific script act as a module too, so that it's importable in tests, you could do a small tweak:
var Router = (function (router) {
router.routeUser = function(user) {
console.log("I'm in! --> " + user)
};
if (typeof exports === "object") {
module.exports = router;
// now the Mocha tests can import it!
}
return router;
})(Router || {});
Long term solution
In the long run, you can get lots of benefits by rewriting all your code to use ONLY modules and use a tool like webpack to package it for you. The above idea is a small step in your direction that gives you one specific benefit (testability). But it is not a long term solution and you'll have some trouble handling dependencies (what if your Router expects some globals to be in place?)
If you intend to run your Mocha tests in the browser, you do not have to alter your existing code.
Let's walk through the IIFE pattern, because based on your code, I think you may misunderstand how it works. The basic shape is this:
var thing = (function() {
return 1;
})();
console.log(thing) // '1'
It's a var declaration setting thing equal to the value on the right side of the equals sign. On the right, the first set of parens wraps a function. Then, a second set of parens sits right next to it, at the end. The second set invokes the function expression contained in the first set of parens. That means the return value of the function will be the right-side value in the var statement. So thing equals 1.
In your case, that means that the outer Router variable is set equal to the router variable returned by your function. That means you can access it as Router in your tests, after including the script in the DOM:
suite('Route User Tests', function () {
test('Route The User', function () {
if (!Router) // <- Notice the capital 'R'
throw new Error("failed!");
else {
Router.routeUser("Me") // <- capital 'R'
}
});
});
If you intend to run your tests with node, see Kos's answer.

Disable Jasmine expectation, like xdescribe or xit?

On the Jasmine website I see that we can disable suites by xdescribe or individual specs by xit. Is there a way to disable only an expectation (like xexpect)?
The reason why I'm asking this is because I'm writing e2e tests with Protractor and in our continuous integration we don't yet (if ever) have access to the database, though locally we can run real end to end tests with access to the database, for example.
I would like to mark individual expectations as optional, depending on a configuration or environment variable. It would be nice to make a switch once, and then create a wrapper around expect, that only fails if we are running the tests locally (with access to the database).
So for example I can create a new spec family:
if (process.env.DB_AVAILABLE) {
dbit = it;
} else {
dbit = xit;
}
and write specs that depend on database connection as following:
dbit('creates new user', function () {});
Is there a way to do the same with expect (e.g. dbexpect)?
If there is something fundamentally wrong with my approach, don't hold it back and let me know.
You could create your own xexpect by implementing all the methods/properties with an empty function:
var xexpect = function() {
return xexpect;
};
Object.getOwnPropertyNames(jasmine.Expectation.prototype).forEach(function(name){
xexpect[name] = xexpect;
});
Object.defineProperty(xexpect, 'not', {get: xexpect});
Usage :
xexpect(1).toBeGreaterThan(2);
xexpect(true).not.toEqual(true);

In Node.js, asking for a value using Prompt, and using that value in a main js file

I'm pretty new to node.js and it seems fairly easy to use but when it comes to getting a value using the command line and returning that value to be used in another package or .js, it seems harder than I expected.
Long story short, I've used a npm package (akamai-ccu-purge), to enter a file to purge on the akamai network successfully.
I want to make it more dynamic though by prompting the user to enter the file they want purged and then using that in the akamai package.
After making a few tries using var stdin = process.openStdin(); I actually found another npm package called Prompt that seemed to be easier. Both ways seem to have the same problem though.
Node doesn't seem to want to stop for the input. It seems to want to automatically make the purge without waiting for input even though I've called that module first. It actually gets to the point where I should enter the file but it doesn't wait.
I am definitely missing something in my understanding or usage here, what am I doing wrong?
My code so far is:
var purgeUrl = require('./getUrl2');
var PurgerFactory = require('../../node_modules/akamai-ccu-purge/index'); // this is the directory where the index.js of the node module was installed
// area where I placed the authentication tokens
var config = {
clientToken: //my tokens and secrets akamai requires
};
// area where urls are placed. More than one can be listed with comma separated values
var objects = [
purgeUrl // I am trying to pull this from the getUrl2 module
];
// Go for it!
var Purger = PurgerFactory.create(config);
Purger.purgeObjects(objects, function(err, res) {
console.log('------------------------');
console.log('Purge Result:', res.body);
console.log('------------------------');
Purger.checkPurgeStatus(res.body.progressUri, function(err, res) {
console.log('Purge Status', res.body);
console.log('------------------------');
Purger.checkQueueLength(function(err, res) {
console.log('Queue Length', res.body);
console.log('------------------------');
});
});
});
The getUrl2 module looks like this:
var prompt = require('../../node_modules/prompt');
//
// Start the prompt
//
prompt.start();
//
// Get property from the user
//
prompt.get(['newUrl'], function (err, result) {
//
// Log the results.
//
console.log('Command-line input received:');
console.log(' http://example.com/custom/' + result.newUrl);
var purgeUrl = 'http://example.com/custom/' + result.newUrl;
console.log(purgeUrl);
module.exports = purgeUrl;
});
Thanks again for the help!
I would probably just allow getURL2 to expose a method that will be invoked in the main module. For example:
// getURL2
var prompt = require('../../node_modules/prompt');
module.exports = {
start: function(callback) {
prompt.start();
prompt.get(['newUrl'], function (err, result) {
// the callback is defined in your main module
return callback('http://example.com/custom/' + result.newUrl);
});
}
}
Then in your main module:
require('./getUrl2').start(function(purgeURL) {
// do stuff with the purgeURL defined in the other module
});
The implementation may differ, but conceptually, you need to make your second module, which requires some sort of input from the user, happen as a result of that input. Callbacks are a common way to do this (as are Promises). However, as prompt is not necessarily exposing a method that would necessitate a Promise, you can do it with plain old callbacks.
You might also want to search around for articles on writing command line tools (sometimes referenced as CLIs) or command line apps with Node. I found the following article to be helpful when trying to figure this out myself:
http://javascriptplayground.com/blog/2015/03/node-command-line-tool/
Also, the command-line-args module worked well for me (though there's a number of other modules out there to choose from):
https://www.npmjs.com/package/command-line-args
Good luck!

How to properly test an AngularJS Controller Function

We just started implementing jasmine tests in our AngularJS project and I have a question:
We want to test this controller function:
$scope.deleteClick = function () {
$scope.processing = true;
peopleNotesSrv.deleteNote($scope.currentOperator.operatorId, $scope.noteId, $scope.deleteSuccessCallback, $scope.deleteErrorCallback);
};
We wrote this following test:
it('deleteClick should pass proper parameters to peopleNoteSrv', function () {
$controllerConstructor('PeopleNoteEditCtrl', { $scope: $scope });
$scope.noteId = 5;
expect(function () { $scope.deleteClick(); }).not.toThrow();
});
This test makes sure that when we call the $scope.deleteClick() function that $scope.processing is set to true and that the call to peopleNotesSrv doesn't throw any errors because of invalid arguments. We are testing the two callback functions in separate tests.
Should we be testing that the peopleNotesSrv.deleteNote function was called so the test is more explicit? The way this test is written right now it doesn't really tell someone what the deleteClick() function does under the hood and that seems to be incorrect.
Ask yourself what you'd do if you had developed it using TDD. It pretty much goes the direction Sam pointed out, but here are some examples:
Controller Tests
start writing a test which would expect a deleteClick to exist.
Expect deleteClick to setup the loading state (check for processing = true)
Test whether a service is injected into the controller (peopleNotesSrv)
Check whether deleteClick calls the service (as already mentioned via spies)
Verify that $scope.noteId and the other $scope.params are present and set
This is as far as it relates to the Controller. All the criteria whether it fails or throws errors etc. should be tested in a Service.spec. Since I don't know your service in detail here some examples
Service Tests
Ensure deleteNote exists
Check what happens if wrong number of arguments (less or more) are supplied
Make some positive tests (like your noteId = 5)
Make some negative tests
Ensure callbacks are properly called
... and so on.
Testing for validity in controllers doesn't make a lot of sense because than you'd need to do it for every Controller you have out there. By isolating the Service as a separate Unit of Test and ensure that it fulfills all the requirements you can just use it without testing. It's kinda the same as you never would test jQuery features or in case of Angular jQLite, since you simply expect them to do what they should :)
EDIT:
Make controller tests fail on service call
Pretty easy lets take this example. First we create our Service Test to ensure that the call fails if not the proper number of arguments is supplied:
describe('Service: peopleNoteSrv', function () {
// load the service's module
beforeEach(module('angularControllerServicecallApp'));
// instantiate service
var peopleNoteSrv;
beforeEach(inject(function (_peopleNoteSrv_) {
peopleNoteSrv = _peopleNoteSrv_;
}));
it('should throw error on false number of arguments', function () {
expect(function() { peopleNoteSrv.deleteNote('justOneParameter'); }).toThrow();
});
});
Now to ensure that the test passes lets create the error throwing part in our service method
angular.module('angularControllerServicecallApp')
.service('peopleNoteSrv', function peopleNoteSrv() {
this.deleteNote = function(param1, param2, param3) {
if(arguments.length !== 3)
throw Error('Invalid number of arguments supplied');
return "OK";
};
});
Now lets create 2 demo controllers, FirstCtrl will do it properly, but SecondCtrl should fail
angular.module('angularControllerServicecallApp')
.controller('FirstCtrl', function ($scope, peopleNoteSrv) {
$scope.doIt = function() {
return peopleNoteSrv.deleteNote('param1', 'param2', 'param3');
}
});
angular.module('angularControllerServicecallApp')
.controller('SecondCtrl', function ($scope, peopleNoteSrv) {
$scope.doIt = function() {
return peopleNoteSrv.deleteNote('onlyOneParameter');
}
});
And both controller as a demo have following test:
it('should call Service properly', function () {
expect(scope.doIt()).toBe("OK");
});
Karma now spits out something like this:
Error: Invalid number of arguments supplied
at [PATH]/app/scripts/services/peoplenotesrv.js:15
at [PATH]/app/scripts/controllers/second.js:13
at [PATH]/test/spec/controllers/second.js:20
Thus you exactly know that you missed to update SecondCtrl. Of course this should work for any of your tests consuming the Service method.
Hope that's what you meant.
I think the answer is that it depends.
There are two cases:
1 - You also have a suite of tests for the peopleNotesSrv service.
In this case I would leave this test as-is or check a few more things around the specific functionality of $scope.deleteClick(), such as if there are any watchers on $scope.processing that do anything specific regarding a .deleteClick() call.
2 - You do not have any tests for all the possible functionality for the peopleNotesSrv service.
In this case I would write a more explicit test that does check that the .deleteNote() actually performed it's job.
In my opinion you should really build tests up and try to not test the same thing in more than one place, as this adds extra work and could produce holes in the tests if you think, "Well I can just test this specific case when it gets called from a specific function that calls it."
What if you ever want to reuse that deletNote() as part of a bigger function in a different place?Then you need to write another test for the same code because it is being called from a different function.
So I would aim for case 1, this way you can write all your tests for that service and then trust that those tests cover the rest of this particular test. If you throw errors on bad input or for failures to actually delete a note, you should trust that other code to test what it was designed to test. This will greatly speed up your test-writing time and increase the chance that your tests cover all the cases. It also keeps all the tests for that service in the same place in your test code.
I think also a good question to start with is what kind of test is this? Unit Test or End-to-End test?
I was assuming it was a Unit Test for my answer, if it was an End-to-End test, then you might want to keep following the function calls to verify everything is happening as you expect.
Here are some links on Unit Tests, End-to-End tests, and a pretty good article about both and Angular.
What's the difference between unit, functional, acceptance, and integration tests? (End-to-End tests can also be called Integration test)
http://www.sitepoint.com/unit-and-e2e-testing-in-angularjs/

Categories

Resources