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();
});
});
Related
I have been trying to read up on writing test methods for void modules where there is a side-effect.
I can't seem to wrap my head around implementing it in my scenario where I have a module that takes in an array of loggers. This is to allow the client to implement multiple sources of logging in particular scenarios like using the console and logging to a persisted logging store (multiple sources if need be).
I have a level of abstraction which maps through concrete sources with the same contract.
The class abstracting from concrete classes has some logic to know which methods to be called and I want to test that that logic is sound but I am not sure how to do so. Since these methods are void am I right in thinking that I may need to call .toHaveBeenCalled to check for these side-effects?
Initialize logger
src/index.js
import logger from "./logger";
logger.init([console]);
logger.debug("Hello debug");
Logger
src/logger.js - abstraction expecting an array of concretes
function init(loggers) {
...
}
function debug(message) {
writeToLogs(loggers, message, "debug");
}
function writeToLogs(loggers, message, type) {
// I want to test this logic - multiple loggers and different calls mapped correctly
loggers.forEach(logger => {
switch (type) {
case "debug":
logger.debug(message);
break;
...
}
}
}
module.exports = {
init: init,
debug: debug,
...
}
Console logger
src/logger/console.js - example of a concrete
function debug(message) {
console.debug(message);
}
module.exports = {
debug: debug,
...
}
Test
tests/logger.test.js
import logger from "../../src/logger";
test("logger writes information", () => {
// What should I do here?
});
The console module is just writing to the console. In my opinion I don't think it is necessary to be tested unless it changes in the future. If I were to test it, how do I verify that the console has been written to? I would see this as an integration test as it is the actual implementation that integrates with the browsers' console.
I am using webpack 4.6.0 on node 9.5.0 with jest 22.4.3.
console is a side effect you have no control over by just mocking an import as it is not imported but an object in the global namespace of your module. Fortunately you can overwrite this from outside using the global object in your test file. So you can easily set anything you want in there
const debug = jest.fn()
const log = jest.fn()
const error = jest.fn()
global.console = {debug, error, log}
Now everytime console.log in your src/logger/console.js is called it will use the spy were you later on can test that it was called.
so now whenever
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
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
I'm a strange behavior with RequireJS using the CommonJS syntax. I'll try to explain as better as possible the context I'm working on.
I have a JS file, called Controller.js, that registers for input events (a click) and uses a series of if statement to perform the correct action. A typical if statement block can be the following.
if(something) {
// RequireJS syntax here
} else if(other) { // ...
To implement the RequireJS syntax I tried two different patterns. The first one is the following. This is the standard way to load modules.
if(something) {
require(['CompositeView'], function(CompositeView) {
// using CompositeView here...
});
} else if(other) { // ...
The second, instead, uses the CommonJS syntax like
if(something) {
var CompositeView = require('CompositeView');
// using CompositeView here...
} else if(other) { // ...
Both pattern works as expected but I've noticed a strange behavior through Firebug (the same happens with Chrome tool). In particular, using the second one, the CompositeView file is already downloaded even if I haven't follow the branch that manages the specific action in response to something condition. On the contrary, with the first solution the file is downloaded when requested.
Am I missing something? Is it due to variable hoisting?
This is a limitation of the support for CommonJS-style require. The documentation explains that something like this:
define(function (require) {
var dependency1 = require('dependency1'),
dependency2 = require('dependency2');
return function () {};
});
is translated by RequireJS to:
define(['require', 'dependency1', 'dependency2'], function (require) {
var dependency1 = require('dependency1'),
dependency2 = require('dependency2');
return function () {};
});
Note how the arguments to the 2 require calls become part of the array passed to define.
What you say you observed is consistent with RequireJS reaching inside the if and pulling the required module up to the define so that it is always loaded even if the branch is not taken. The only way to prevents RequireJS from always loading your module is what you've already discovered: you have to use require with a callback.
After a brief romance with the revealing module pattern I've come to realise a set-back when it comes to unit-testing modules. I cannot however decide if it is my approach to testing a module or whether there is some form of work-around.
Consider the following code:
var myWonderfulModule = (function () {
function publicMethodA (condition) {
if(condition === 'b') {
publicMethodB();
}
}
function publicMethodB () {
// ...
}
return {
methodA : publicMethodA,
methodB : publicMethodB
}
}());
If I wanted to test (using Jasmine) the various paths leading through publicMethodA to publicMethodB. I might write a small test like so:
it("should make a call to publicMethodB when condition is 'b'", function() {
spyOn(myWonderfulModule , 'publicMethodB');
myWonderfulModule.publicMethodA('b');
expect(myWonderfulModule.publicMethodB).toHaveBeenCalled();
});
If I understand correctly, there's a copy of publicMethodB within the closure that cannot be changed. Even if I change myWonderfulModule.publicMethodB afterwards:
myWonderfulModule.publicMethodB = undefined;
calling myWonderfulModule.publicMethodA will still run the original version of B.
The example above is of course simplified but there are plenty of scenarios I can think of where it would be convenient to unit test conditional paths through a method.
Is this a limitation of the revealing module pattern or simply a misuse of unit testing? If not what work-arounds are available to me? I'm considering moving to something like RequireJS or reverting back to non-modular code.
Any advice appreciated!
You cant test the intern methodes of a closure. And you also shouldn't spy on it. Think about about your module as a black box. You put something in and you get something out. All you should test is that the thing you get out of your module is the one that you expect.
Spying on methodes in your module makes not much sense. Think about it. You spy on it, the test passes. Now you change the functionality so it creates a bug, the test still passes cause the function is still called but you never mention the bug. If you just test the thing that cames out you dont need to spy on internal methodes cause, that they are called is implicite when the outcome of the module is what you expect.
So in your case there is no thing that goes in and nothing comes out. This makes not much sense but I believe that your module interacts with DOM or makes an ajax call. This are things that you can test (DOM) or you should spy on (ajax).
You should also make you self familiar with Inversion of Control and Dependency Injection. These are patterns that will make your modules much more easier to test.
If you use the keyword "this" when you call publicMethodB() from publicMethodA() it will work. For example:
var myWonderfulModule = (function () {
function publicMethodA (condition) {
if(condition === 'b') {
this.publicMethodB();
}
}
function publicMethodB () {
// ...
}
return {
methodA : publicMethodA,
methodB : publicMethodB
}
}());