I am working with a Jasmine testing suite that includes both "vanilla" Jasmine tests along with Jasmine tests for some Angular 2 components. Because of Angular 2's inclusion, zone.js gets loaded. This creates a conflict with Jasmine's clock. For example, the following test fails with the error, Error: Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?
describe('an async test with zone.js present', function() {
beforeEach(function() {
jasmine.clock().install();
});
afterEach(function() {
jasmine.clock().uninstall();
});
it('cannot install jasmine\'s mock clock', function() {
var callback = jasmine.createSpy('setTimeoutCallback')
setTimeout(callback, 55);
jasmine.clock().tick(56);
expect(callback).toHaveBeenCalled();
});
})
Here is plunker for above code.
Short of delivering the Angular 2 tests separately from the "vanilla" tests, I am wondering what options might be available. For example, is it possible to perform the Jasmine clock's job with the zone? For example, is it possible to simulate the tick with the zone or flush all of the scheduled tasks before the assertion?
For me it works if you uninstall the clock in beforeEach. Its not recommended by jasmine and a bit strange because for uninstall it makes more sense to use afterEach. But calling uninstall before the first install call happens fixed it for me.
As stated on Angular documentation you should use tick function in a fakeAsync body which is part of the #angular/core/testing module.
Using your example and TypeScript it would look something like this...
import { fakeAsync, tick } from '#angular/core/testing';
...
it('cannot install jasmine\'s mock clock', fakeAsync(() => {
var callback = jasmine.createSpy('setTimeoutCallback')
setTimeout(callback, 55);
tick(56);
expect(callback).toHaveBeenCalled();
}));
The code which throws this here.
It implies that jasmine was loaded before Zone.js. Switch the loading order. Zone always needs to be loaded first.
Here's the fixed fork of your plunker:
<script data-require="zone.js#*" data-semver="0.4.1" src="https://cdn.rawgit.com/angular/zone.js/v0.4.1/zone.js"></script>
<script data-require="zone.js#*" data-semver="0.4.1" src="https://cdn.rawgit.com/angular/zone.js/v0.4.1/long-stack-trace-zone.js"></script>
<script data-require="jasmine#*" data-semver="2.4.1" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.4.1/boot.js"></script>
https://plnkr.co/edit/6iuvWFOZLqHWJIo4
This have been resolved by https://github.com/angular/zone.js/pull/1009.
zone.js and future angular will support jasmine.clock().
Related
I'm writing tests for a JS application using Jasmine and testdouble.js as a mocking library. I am using AMD format to organize code in modules, and RequreJS as a module loader. I was wondering how to use testdouble.js to replace dependency for the module being tested that is in AMD format and it is loading via RequireJS. The documentation is unclear about this or I am missing something, so if someone could point me in the right direction.
I'll post the example bellow that illustrates my setup and the problem that I am facing.
car.js
define("car", ["engine"], function(engine) {
function drive = {
engine.run();
}
return {
drive: drive
}
});
engine.js
define("engine", function() {
function run() {
console.log("Engine running!");
}
return {
run: run
}
});
car.spec.js
define(["car"], function(car) {
describe("Car", function() {
it("should run the motor when driving", function() {
// I am not sure how to mock the engine's object run method
// and where to place that logic, in beforeEach or...
td.replace(engine, "run");
car.drive();
// How to verify that when car.run() has executed, it calls this mocked method
td.verify(engine.run());
});
});
});
testdouble.js does not have any explicit support for AMD modules. The only module-related tricks it offers are Node.js specific and built on top of Node's CJS module loader.
What you would need to do in this case is require from the test a reference to engine and replace the run property, which it seems like you've done (your example is incomplete).
If you do this, don't forget to run td.reset() in an afterEach to restore the original properties to anything you replace!
I want to be able to have all my describe statements in Mocha get kicked off in parallel. Can someone help me figure out how to do that?
You can't do this directly with mocha because it creates a list of it() callbacks and invokes them in order.
mocha-parallel-tests can do this if you're willing to move your describes into separate .js files. To convince yourself, install it somewhere and invoke it with a short --slow so it reports each time:
laptop:/tmp/delme$ npm install mocha-parallel-tests
laptop:/tmp/delme$ cd node_modules/mocha-parallel-tests
laptop:/tmp/delme/node_modules/mocha-parallel-tests$ ./bin/mocha-parallel-tests test/parallel/tests --timeout 10000 --slow 100
You will see that it ran three (very simple) tests suites in the time it took to run the longest.
If your tests don't depend on side-effects of earlier tests, you can make them all asynchronous.
A simple way to do this is to initiate the stuff that takes a while before the describe and use the regular mocha apparatus to evaluate it. Here, I create a bunch of promises which take a while to resolve and then iterate through the tests again, examining their results in a .then() function:
var expect = require("chai").expect;
var SlowTests = [
{ name: "a" , time: 250 },
{ name: "b" , time: 500 },
{ name: "c" , time: 750 },
{ name: "d" , time:1000 },
{ name: "e" , time:1250 },
{ name: "f" , time:1500 }
];
SlowTests.forEach(function (test) {
test.promise = takeAWhile(test.time);
});
describe("SlowTests", function () {
// mocha defaults to 2s timeout. change to 5s with: this.timeout(5000);
SlowTests.forEach(function (test) {
it("should pass '" + test.name + "' in around "+ test.time +" mseconds.",
function (done) {
test.promise.then(function (res) {
expect(res).to.be.equal(test.time);
done();
}).catch(function (err) {
done(err);
});
});
});
});
function takeAWhile (time) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(time);
}, time);
});
}
(Save this as foo.js and invoke with mocha foo.js.)
Meta I disagree with the assertion that tests should be primarily be synchronous. before and after pragmas are easier but it's rare that one test invalidates all remaining tests. All discouraging asynchronous tests does is discourage extensive testing of network tasks.
Mocha does not support what you are trying to do out of the box. It runs tests sequentially. This has a big advantage when dealing with an unhandled exception: Mocha can be sure that it happened in the test that it is currently running. So it ascribes the exception to the current test. It is certainly possible to support parallel testing but it would complicate Mocha quite a bit.
And I tend to agree with David's comment. I would not do it. At the level at which Mocha usually operates, parallelism does not seem to me particularly desirable. Where I have used test parallelism before is at the level of running end-to-end suites. For instance, run a suite against Firefox in Windows 8.1 while at the same time running the same suite against Chrome in Linux.
Just to update this question, Mocha version 8+ now natively supports parallel runs. You can use the --parallel flag to run your tests in parallel.
Parallel tests should work out-of-the box for many use cases. However, you must be aware of some important implications of the behavior
1 thing to note, some reporters don't currently support this execution (mocha-junit-reporter for example)
If you are using karma to start your tests, you can use karma-parallel to split up your tests across multiple browser instances. It runs specs in different browser instances in parallel and is very simple and easy to install:
npm i karma-parallel
and then add the 'parallel' to the frameworks list in karma.conf.js
module.exports = function(config) {
config.set({
frameworks: ['parallel', 'mocha']
});
};
karma-parallel
Disclosure: I am the author
I'm trying to use the methods beforeAll and afterAll of jasmine, to create a suite of tests with frisby.js, because actually, frisby doesn't have a support for this methods. So, this is what I'm trying to do:
var frisby = require('frisby');
describe("setUp and tearDown", function(){
beforeAll(function(){
console.log("test beforeAll");
});
afterAll(function(){
console.log("afterAll");
});
//FRISBY TESTS
}); //end of describe function
If I change the methods before/afterAll to before/afterEach, is working, but when I'm using before/afterAll this error appears on console:
Message:
ReferenceError: beforeAll is not defined
Stacktrace:
ReferenceError: beforeAll is not defined
I have the jasmine version 2.3.2 installed on my project, so, I don't know what I need to do to integrate this method.
Use the jasmine library not the jasmine-node library. The second one does not support beforeAll and afterAll methods.
1- npm install -g jasmine
2- jasmine init
3- write the test in the spec folder:
describe("A spec using beforeAll and afterAll", function() {
var foo;
beforeAll(function() {
foo = 1;
});
afterAll(function() {
foo = 0;
});
it("sets the initial value of foo before specs run", function() {
expect(foo).toEqual(1);
foo += 1;
});
it("does not reset foo between specs", function() {
expect(foo).toEqual(2);
});
});
4- Run the tests --> jasmine
The current version of frisby doesnt suport this kind of setup. The community, like myself is eager to this feature like in this issue describes.
The team is working on this feature, but it will come in version 2 of the package that is in the way for more than a year now. More info at this link.
I have a JavaScript project which I want to observe the TDD methodology. I chose the karma framework and requirejs library for this and followed an example demonstrated in the karma docs here.
There is an example of one unit-test file, which is:
define(['app', 'jquery', 'underscore'], function(App, $, _) {
describe('just checking', function() {
it('works for app', function() {
var el = $('<div></div>');
var app = new App(el);
app.render();
expect(el.text()).toEqual('require.js up and running');
});
it('works for underscore', function() {
// just checking that _ works
expect(_.size([1,2,3])).toEqual(3);
});
});
});
The main problem with this approach is that there is no way to clear the App module for each test. So if there are some changes in the App (like closure variables changes etc.) in one it call, then they can affect other it call.
Could anyone suggest something on this? These are such popular tools, I can't believe no one ever ran into such a situation.
So, to recapitulate the question, I would like to ask for an answer for any of these more specific ones:
is there any way to "reset" (clear) module in requirejs? (actually, I suppose that there is no such a way, except reload all modules once again)
is there any better approach to run karma and requirejs, so that modules do not have any remains (side effects) of other tests (it function calls) on them ?
With some help from Google I've found a solution to this. I cannot say that it is ideal, but it works at least and in each test a required module is in it's pristine state.
First, one need to have his main.js test file like this, it is almost the same as in the docs, except tests are not defined as modules and are not included as dependencies:
require.config({file
baseUrl: '/base',
paths: {},
shim: {},
deps: [],
callback: window.__karma__.start
});
In the main karma config must be present this (one need to adapt his own paths)
files: [
{pattern: 'src/**/*.js', included: false},
'tests/unit/**/*.js'
],
And in the tests themselves we leverage jasmine async tests capabilities and requirejs.undef method for "clearing" a module (which will reset the loader's internal state to forget about the previous definition of the module, as it is stated in the docs).
This method has one implication, that it will not reset other modules, which are already loaded and depend on the reset module, but this should not be a problem, if one writes his tests in a right way, that is he tests only one module (and may be some parent ones, from which the child inherits some behavior), so that it should not be hard to undef several modules at ones.
describe('Some/Module tests', function() {
'use strict'
var M
beforeEach(function(done) {
requirejs.undef('src/Some/GParentModule')
requirejs.undef('src/Some/ParentModule')
requirejs.undef('src/Some/Module')
require(['src/Some/Module'], function(m) {
M = m
done()
})
})
it('some description', function() {
var i = 0
for (var prop in M) {
i++
}
expect(i).toEqual(1)
expect(prop).toEqual('init')
expect(M.init).toEqual(jasmine.any(Function))
})
})
I've checked this, between it calls changes to the module do not persist, so I suppose, it's safe to use this approach.
Thanks everyone for help, if someone has a better way, please answer here.
is there any way to "reset" (clear) module in requirejs?
You can use requirejs.undef(), but the documentation mention some gotchas. And to my knowledge there is no call you can use to say "unload this module and everything it depends on", which in a complex application is probably what is needed.
is there any better approach to run karma and requirejs, so that modules do not have any remains (side effects) of other tests (it function calls) on them ?
The better approach is to design your application so that state is tied to an object you instantiate rather than to the module itself. This is what I do in the test suite for a large application of mine. The beforeEach hook resets what requires resetting and instantiates the application anew for each test.
I'm currently running my test suite on AngularJS using Grunt, Karma, Jasmine and Protractor. The database library I'm using is hood.ie, which is a library on top of CouchDB. I start hood.ie using the following code in my Gruntfile:
hoodie: {
start: {
options: {
callback: function(config) {
grunt.config.set('connect.proxies.0.port', config.stack.couch.port);
}
}
}
},
However, I would like to have a separate database for running tests, which automatically resets afterwards. This way, the production data won't conflict with the tests.
How should I approach this? I would assume there's some kind of standard way of doing this, as I can imagine other people have come across the same problem, but I'm unable to find anything on the internet.
Currently, this seems to be impossible as the hoodie server does not support it. The best way to go about this is to modify it yourself at Hood.ie server Github repository by adding a parameter to define the folder in which the data will be stored, which is at the moment hardcoded to 'data' (https://github.com/hoodiehq/hoodie-server/blob/master/lib/core/environment.js#L48)
Something similar to this should work:
app_path: path.resolve(project_dir, argv.folder || 'data')
As the hoodie task is a 'multitask' you could have a test target in your hood.ie grunt task specific to testing, and then reference this in a grunt command used to run tests e.g:
hoodie: {
start: {
options: {
callback: function(config) {
grunt.config.set('connect.proxies.0.port', config.stack.couch.port);
}
}
},
test: {
options: {
callback: function(config) {
// Make test specific changes here.
}
}
}
}
// The task that runs tests first starting test deps. 'runtests' can be anything you want.
grunt.registerTask('test', 'Run unit tests', ['hoodie:test', 'runtests']);
Note: this will mean any other times you're referencing the hoodie task you'll need to be explicit as otherwise all the specified targets will be run. See this documentation on multitasks for more info. In this example you'd change hoodie to hoodie:start to run the 'start' task as previously defined.