I am trying to run coverage tests for my JavaScript unit tests (written in QUnit [not my idea, legacy project]) and run them via command line.
Problem i am facing is references to external bits of code.
Look at example bellow:
Code:
var DateHelper = function() {
return {
GetUtcDate: function (date) {
if (DateTypeChecker.Data["date"]) {
return new Date();
}
return date;
}
}
Test:
QUnit.test('GetUtcNow - compare UTC date', function(assert) {
var currentUtcDate = DateHelper.GetUtcNow();
var nowDate = new Date();
assert.equal(nowDate.getUTCFullYear() == currentUtcDate.getFullYear(), 'dates are the same');
});
So a very simple test that checks two dates, this works lovely when run in the browser, because the external javascript file containing DateTypeChecker is loaded in the HEAD.
But this test fails in the command line as node (or w/e is performing the test) doesn't have a reference to DateTypeChecker object.
My question is, how do i fix this, so that the file for DateTypeChecker is loaded/required? (i know that i can do this with RequireJS, but i don't wan't to add more dependencies and frameworks)
I think this is a general question for js unit testing (not just QUnit).
Two options: (a) mock out the external dependency or (b) actually include the external dependency JS file. For the second option (b), I would use a test runner like Karma and hook up QUnit to it. The configuration is pretty straight forward on that linked site.
The first option (a) is what I would recommend, however. It makes your tests more idempotent and thus loosely coupled. In QUnit you can set up a mock pretty easy, we just use a simple beforeEach function:
QUnit.module( "testing DateHelper", {
beforeEach: function() {
// before each test runs, we can create a "fake" DateTypeChecker
// Note that it doesn't matter what this thing does, so long as it
// satisfies your dependency in the DateHelper
window.DateTypeCheker = {
Data: { date: true }
};
}
});
QUnit.test( ... ); // your normal stuff
Related
I'm having a hard time figuring out how I can access a function that is usually available as a method of the window object in the browser. I'm using Karma, Headless Chrome and Jasmine.
Here is a simplification of my issue:
I have a module called add-numbers-together.js:
function addTogether(a, b) {
return a + b;
}
(function(){
addTogether(2,5);
})();
That is being tested by this Jasmine test:
describe('add-numbers-together.js', function() {
it('Should add numbers together', function() {
require('module/add-numbers-together');
console.info(window.addTogether);
});
});
The require statement is definitely retrieving the module ok, I have tested that.
I was expecting the console.info to print out the definition of the function, as is the case when I do this in an actual browser, but instead this console.info returns undefined. Why is this? and how can I access the addTogether function after the module has been required in the test?
I'm guessing this is some quirk of Jasmine or Headless Chrome but I can't find the answer anywhere no matter how hard I search!
Also, please note: I do not want to add any test code to the module itself, that is not an option for me.
I changed the test slightly to check that the function is not undefined, but it failed regardless of Chrome or ChromeHeadless as the browser.
describe('add-numbers-together.js', function() {
it('Should add numbers together', function() {
require('module/add-numbers-together');
expect(window.addTogether).not.toBeUndefined();
});
});
To get a version of this test passing, you have several options:
Make the code under test a real module and export the function, then change the test to import that module (recommended). This can also be define and require if using AMD.
Configure your build setup to force a global export from that module without changing the code (webpack can do this, and it's kind of hackey, but it works if you can't edit that script). This is usually only done with 3rd party scripts.
Put the script content in the test file above the test code (not recommended, since you are copy-pasting and changes won't be synced up).
Load the script under test and then the test in the fixture markup (works, but kind of lame since your test fixture HTML now hard codes a script reference).
Here is the code under test rewritten for option #1 with the export of the function.
export function addTogether(a, b) {
return a + b;
}
(function () {
addTogether(2, 5);
})();
And here is the test rewritten for option #1 with the import of the module under test.
import * as addNumbersTogether from 'add-numbers-together';
describe('add-numbers-together.js', function () {
it('Should add numbers together', function () {
expect(addNumbersTogether.addTogether).not.toBeUndefined();
});
});
Let's say you have a simple mocha test:
describe("Suite", function(){
it("test",function(doneCallback){
// here be tests
});
});
In this test I can change the timeout by adding this.timeout(VALUE); anywhere within the describe function.
However, besides the timeout value, there are plenty of other Mocha options that can be exclusively declared either from the command line or from a mocha.opts file that lives in the test folder (./test/mocha.opts).
What I want is to change some of these options at run-time (for example, the reporter) and not in command line / mocha.opts file.
From my research of what's possible, I found that there is an article explaining how you can use mocha programmatically, which would allow changing these options at run-time, but you need to create the Mocha instance yourself, whereas in an ordinary test one doesn't have direct access to the Mocha instance.
So, is there a way to get the Mocha instance from an existent test and change some of these options like reporter at run-time during a test?
I would like to have an option that doesn't require to modify the source code of Mocha in any way (I suppose I could tamper with the Mocha instance to implement a way to get an instance directly in the Mocha constructor).
The best way that you can achieve that is by using Mocha as per the wiki link that you have already referenced, which is using Mocha programmatically.
So to your inquiry on changing the reporter parameter here is a brief example that would do what you want, in order to run the tests against a theoretically already existing file named test-file-a.js that contains your tests:
var Mocha = require('mocha'),
mocha = new Mocha(),
path = require('path');
mocha.addFile(path.join(__dirname, 'test-file-a.js'));
mocha
.reporter('list')
.run();
Besides that there are plenty other options that you can use and also there are some listeners for events, like test that you may want to do something during a test, for example:
mocha
.reporter('list')
.ui('tdd')
.bail()
.timeout(10000)
.run()
.on('test', function(test) {
if (test.title === 'some title that you want here') {
//do something
}
});
Please note that you can define the options per each Mocha instance that will run again a test suite, but not during the runtime of a test suite, so for example if you start your tests for test-file-a.js with the option reporter('list') as above you cannot change it while the tests are running to something else, like you may do for example with the timeout option where you can do this.timeout().
So you would have to instantiate a new Mocha instance as the examples above with different options each time.
No, you cannot. without changing the code.
In short, mocha is created in a scope you cannot access from tests. Without going in details, the objects provided in your scope cannot change the options you want. (You cannot do this: link)
But there is a way to define your own reporter and customize the output for each test:
Create a file called MyCustomReporter.js:
'use strict';
module.exports = MyCustomReporter;
function MyCustomReporter (runner) {
runner.on('start', function () {
var reporter = this.suite.suites["0"].reporter;
process.stdout.write('\n');
});
runner.on('pending', function () {
process.stdout.write('\n ');
});
runner.on('pass', function (test) {
var reporter = this.suite.useReporter;
if(reporter == 'do this') {
}
else if(reporter == 'do that'){
}
process.stdout.write('\n ');
process.stdout.write('passed');
});
runner.on('fail', function () {
var reporter = this.suite.useReporter;
process.stdout.write('\n ');
process.stdout.write('failed ');
});
runner.on('end', function () {
console.log();
});
}
When you run mocha, pass the path of MyCustomReporter.js as reporter parameter(without .js), eg:
mocha --reporter "/home/user/path/to/MyCustomReporter"
The default mocha script actually tries to require a reporter file if it is not found in the default ones(under lib/reporters), github link
Finally, in your tests, you can pass some parameters to customize the output of your reporter:
var assert = require('assert');
describe('Array', function() {
describe('#indexOf()', function() {
this.parent.reporter = 'do this';
it('should return -1 when the value is not present', function() {
this.runnable().parent.useReporter = 'do this';
assert.equal([1,2,3].indexOf(4), -1);
});
});
});
I have my code:
var User = function() {
...
}
and the test code using IIFE:
(function() { // test new user
var user = new User();
if (typeof user === 'undefined') {
console.log("Error: user undefined");
}
...
}());
both in the same js file. Works great! But, as the program grows, this is becoming too refractory for me to manage, as I have a piece of test code for every piece of business logic.
I've been taught to keep all my js in the same file, (minified is good) in production, but is there a best-practical way to keep my test code in a different file during development?
I was thinking I could use a shell script to append the test code to the production code when I want to run the tests, but I'd prefer a cross-platform solution.
I don't want or need a framework solution, I want to keep it light -- does node have anything built-in for this sort of thing?
Node has two expressions for this case. First:
module.exports = name_of_module;
Which is to export module for example function, object or something similar. And the second:
var module_name = require('Path/to/module');
to import it from other file. If you want to export IIFE you must assign it to global variable and module.export name of variable.
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);
I'm trying to run Jasmine client integration tests on a meteor project. I'm using meteor 0.9.4, and the sanjo:jasmine package for Jasmine.
I have written a test which looks like:
describe("Template.dashboard.tasks", function() {
it("ela displays correct assessment", function() {
Session.set("selected_subject", "math");
Session.set('selected_grade', "1");
tasks = Template.dashboard.tasks();
expect(true).toBe(true);
});
});
I get an error before it can get to the end of the test:
Cannot read property 'tasks' of undefined
This means that Template.dashboard does not exist within the scope of this test.
Template.dashboard.tasks() is a helper function which works completely, and it is in a js file within a view folder. Regular Jasmine tests work as expected, but as soon as I try to use one of my own functions from another file, it doesn't work.
My question is: Is there something I need to do to give the Jasmine test access to my template helper functions?
In Meteor, Template helper functions used to be formatted like this:
Template.dashboard.tasks = function () {
...
};
But that has been deprecated, and the new format is:
Template.dashboard.helpers({
tasks: function(){
...
}
});
In Jasmine, with the previous formatting, you could access helper functions like:
Template.dashboard.tasks();
But now you must call helper functions like this:
Template.dashboard.__helpers[' tasks']();
Sanjo (the original author of the meteor-jasmine repo) suggested using a function like this to make it easier to call helper functions (especially if the syntax ends up getting changed again):
function callHelper(template, helperName, context = {}, args = []) {
template.__helpers[` ${helperName}`].apply(context, args);
}
An updated answer to this question for Meteor 1.3 (sorry I use mocha, but it does not affect the answer):
Template.foo and Template.foo helpers won't be eagerly set up when testing, so you need to import foo.html then foo.js.
Here is an example :
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { Foo } from '/collections/foo.js';
import { assert } from 'meteor/practicalmeteor:chai';
import './foo.html'; // now Template.foo is defined
import './foo.js'; // now Template.foo.__helpers[' bar'] is defined
describe('foo handlers', () => {
it("Should test bar", () => {
// don't forget the space, helper key is ' bar' not 'bar'
var bar = Template.foo.__helpers[' bar'].apply({foo:"bar"},3);
assert.Equal(bar,'bar');
});
});
Of course as said before, you should definitely encapsulate the weird Template.foo.__helpers[' bar'].apply(context,args) into a nice, clean helper.
tests on server part are running nicely from start, and there is indeed one more thing to do in order to run tests on the frontend part. I'll try to find you that.
In addition, consider reading or reading again the famous and explicit article from Dr. Llama's Blog related to Jasmin/Meteor : Bullet-proof Meteor applications with Velocity, Unit Testing, Integration Testing and Jasmine