Mocha + RequireJS = AMD testing - javascript

I have a hard time connecting Mocha to RequireJS based application, may be You'll be able to come up with something :). After many hours when I've been trying to load AMD modules and simply console.log some 'fired' info that the module has been loaded... nothing happend had happened - the program just ended and printed out some mocha info.
var facade = requirejs(['../../public/js/scripts/widgets/widgets/article/main.js'],
function(mod) {
console.log('fired')
});
// run with: $ mocha -u tdd test.js --reporter spec
and than I've come up with the idea to fire just this to test callbacks:
setTimeout((function() {
console.log('fired');
}), 5000);
// run with: $ mocha -u tdd test.js --reporter spec
also didn't work. So finally I've run both with
$ node test.js
and finally it worked. So question is than: How to run Mocha test with callbacks handling, as those are essential for AMD testing?

The way you are doing it, mocha is not going to do anything with your file because it does not see a test suite in it. RequireJS is scheduled to call the callback but mocha exits before this has a chance to happen. Same with your timeout example.
The following gives you an example.
File test.js:
'use strict';
var requirejs = require("requirejs");
requirejs.config({
baseUrl: '.',
nodeRequire: require
});
suite('Something', function(){
var foo;
suiteSetup(function (done){
// This saves the module foo for use in tests. You have to use
// the done callback because this is asynchronous.
requirejs(['foo'],
function(mod) {
console.log("fired!");
foo = mod;
done();
});
});
suite('blah', function(){
test('blah', function(){
if (foo.test !== "test")
throw new Error("failed!");
});
});
});
File foo.js:
define(function () {
return {test: "test"};
});
When you run:
mocha -u tdd test.js
You'll see that the callback is fired and the test passes.
For the benefit of people reading this question and confused by the use of suite, suiteSetup, test... Mocha supports multiple interfaces. The code here is using the TDD interface (the OP invokes Mocha with -u tdd), which exports suite, suiteSetup, test, etc. In the default BDD interface, the equivalents are describe, before and it, respectively.

I have configured related boilerplate for using mocha in environment of RequireJS. It may be not exact what you want, but it may be helpful.
https://github.com/x2es/boilerplate-karma-mocha-chai-requirejs
One more note - assuming that your script placed in "/public" it makes sense to test it in browser environment instead nodejs. For this purposes you should to look at some test-runner like JsTestDriver (https://code.google.com/p/js-test-driver/) or karma-runner (http://karma-runner.github.io/). Or another...
In snipped provided in karma documentation (http://karma-runner.github.io/0.8/plus/RequireJS.html)
var tests = [];
for (var file in window.__karma__.files) {
if (window.__karma__.files.hasOwnProperty(file)) {
if (/Spec\.js$/.test(file)) {
tests.push(file);
}
}
}
requirejs.config({
// Karma serves files from '/base'
baseUrl: '/base/src',
paths: {
'jquery': '../lib/jquery',
'underscore': '../lib/underscore',
},
shim: {
'underscore': {
exports: '_'
}
},
// ask Require.js to load these files (all our tests)
deps: tests,
// start test run, once Require.js is done
callback: window.__karma__.start
});
introduced way when we force requirejs to preload all necessary spec-files using
require.config({
deps: ['array', 'of', 'our', 'spec', 'files']
})
In this environment each spec-file should be a regular RequireJS module.
Example of test spec for such environment:
define(['chai'], function(chai) {
var expect = chai.expect;
describe('bootstrap', function() {
it('should...', function() {
expect('a').to.equal('a');
});
});
});

Related

Run Jasmine tests written in TypeScript

I have a Typescript+Node+Angular2+Electron app and currently trying to run tests for node classes, written also in Typescript.
For building the application and running it within electron I use following tsconfig:
"compilerOptions": {
"module": "system",
"target": "es6",
...
}
So as you can see, it's using systemjs and compiling TS into JS-es6. It works fine, application itself is working.
Now I need Jasmine to come on board. I installed this npm package, updated my gulp tasks to run gulp-jasmine for just 1 file:
gulp.task('jasmine', function() {
gulp.src('./test/test.js')
.pipe(jasmine())
});
This is how my test.js looks like:
System.register(["./models-src/app/models/pathWatch/pathWatch"], function(exports_1, context_1) {
"use strict";
var __moduleName = context_1 && context_1.id;
var pathWatch_1;
return {
setters:[
function (pathWatch_1_1) {
pathWatch_1 = pathWatch_1_1;
}],
execute: function() {
describe("Run Application:", () => {
it("starts", () => {
var pw1 = new pathWatch_1.PathWatch();
expect(true).toEqual(true);
});
});
}
}
});
So, nothing special, 1 import-1test-1assert, wrapped with SystemJs stuff.
When I try to run this test, I have an error: "System is not defined".
My questions are:
1) Is it possible to run jasmine tests, using systemjs loader inside?
2) If it's possible, do I need to install/configure some additional stuff?
3) I tried to compile TS using Module="commonjs" and it's working. But I don't want to compile my source differently for tests and build. Why it's working fine with commonjs without any additional manipulations?
4) Also I tried to compile TS using Module="es6". It's not working, I have an error "Unexpected reserved word". Is it possible to run jasmine tests written in js es6 without transpiling them into es5?
Thanks a lot!
1) Is it possible to run jasmine tests, using systemjs loader inside?
2) If it's possible, do I need to install/configure some additional
stuff?
You mean, run jasmine tests in node using systemjs as a loader? I don't think jasmine supports using systemjs instead of require for loading modules. So your tests need to be in commonjs, but test code can use SystemJS to load and test application code. Something like this in test.js could work, provided that systemjs is configured properly and can find pathWatch module:
describe("Run Application:", () => {
it("starts", (done) => {
var system = require('systemjs');
system.config({
// systemjs config here
//
});
system.import('path-to-path-watch-module').then(pathWatch => {
var pw = new pathWatch.PathWatch();
expect(true).toEqual(true);
done();
});
});
});
system.import is asynchronous, so all jasmine tests need to be async too.
3) I tried to compile TS using Module="commonjs" and it's working. But
I don't want to compile my source differently for tests and build. Why
it's working fine with commonjs without any additional manipulations?
Because then there is no reference to System in the compiled code - it uses module.exports like any other node module and can be loaded as is by jasmine.
4) Also I tried to compile TS using Module="es6". It's not working, I
have an error "Unexpected reserved word". Is it possible to run
jasmine tests written in js es6 without transpiling them into es5?
Module="es6" requires a runtime that supports es6 import and export, so it needs a transpiler and module loader before it can run on current version of node.

How to properly require modules from mocha.opts file

I'm using the expect.js library with my mocha unit tests. Currently, I'm requiring the library on the first line of each file, like this:
var expect = require('expect.js');
describe('something', function () {
it('should pass', function () {
expect(true).to.be(true); // works
});
});
If possible, I'd like to remove the boilerplate require code from the first line of each file, and have my unit tests magically know about expect. I thought I might be able to do this using the mocha.opts file:
--require ./node_modules/expect.js/index.js
But now I get the following error when running my test:
ReferenceError: expect is not defined
This seems to make sense - how can it know that the reference to expect in my tests refers to what is exported by the expect.js library?
The expect library is definitely getting loaded, as if I change the path to something non-existent then mocha says:
"Error: Cannot find module './does-not-exist.js'"
Is there any way to accomplish what I want? I'm running my tests from a gulp task if perhaps that could help.
You are requiring the module properly but as you figured out, the symbols that the module export won't automatically find themselves into the global space. You can remedy this with your own helper module.
Create test/helper.js:
var expect = require("expect.js")
global.expect = expect;
and set your test/mocha.opts to:
--require test/helper
While Louis's answer is spot on, in the end I solved this with a different approach by using karma and the karma-chai plugin:
Install:
npm install karma-chai --save-dev
Configure:
karma.set({
frameworks: ['mocha', 'chai']
// ...
});
Use:
describe('something', function () {
it('should pass', function () {
expect(true).to.be(true); // works
});
});
Thanks to Louis answer and a bit of fiddling around I sorted out my test environment references using mocha.opts. Here is the complete setup.
My project is a legacy JavaScript application with a lot of "plain" js files which I wish to reference both in an html file using script tags and using require for unit testing with mocha.
I am not certain that this is good practice but I am used to Mocha for unit testing in node project and was eager to use the same tool with minimal adaptation.
I found that exporting is easy:
class Foo{...}
class Bar{...}
if (typeof module !== 'undefined') module.exports = { Foo, Bar };
or
class Buzz{...}
if (typeof module !== 'undefined') module.exports = Buzz;
However, trying to use require in all the files was an issue as the browser would complain about variables being already declared even when enclosed in an if block such as:
if (typeof require !== 'undefined') {
var {Foo,Bar} = require('./foobar.js');
}
So I got rid of the require part in the files and set up a mocha.opts file in my test folder with this content. The paths are relative to the root folder:
--require test/mocha.opts.js
mocha.opts.js content. The paths are relative to the location of the file:
global.assert = require('assert');
global.Foo = require("../foobar.js").Foo;
global.Bar = require("../foobar.js").Bar;
global.Buzz = require("../buzz.js");

How to set execution order of mocha test cases in multiple files

I have two javascript files which contain mocha test cases.
//----------abc.js -------------
describe("abc file", function(){
it("test 1" , function(){
assert.equal(20 , 20);
});
});
//---------xyz.js--------------
describe("xyz file", function(){
it("test 1" , function(){
assert.equal(10 , 10);
});
});
I have put them in a folder called test and when I execute the mocha command the first file(abc.js) is always executed before xyz.js.
I thought this might be due to alphabetical ordering and renamed the files as
abc.js => xyz.js
xyz.js => abc.js
but still, the content of the xyz.js (previously abc.js) is executed first. How can I change the execution order of these test files?
In the second file, require the first one:
--- two.js ---
require("./one")
or if you are using ES modules:
--- two.js ---
import "./one"
Mocha will run the tests in the order the describe calls execute.
I follow a totally seperate solution for this.
Put all your tests in a folder named test/ and
Create a file tests.js in the root directory in the order of execution
--- tests.js ---
require('./test/one.js')
require('./test/two.js')
require('./test/three.js')
And in the tests files one.js, two.js and so on write your simple mocha tests
this way if you want to run them in the order you have defined then just run mocha tests.js
Mocha has a --sort (short -S) option that sorts test files:
$ mocha --help
[...]
-S, --sort sort test files
[...]
Since mocha sorts files in alphabetical order, I usually prefix my test files names with numbers, like:
0 - util.js
1 - something low level.js
2 - something more interesting.js
etc.
In addition to being really easy to maintain (no gulp grunt or any of that nonsense, no editing your package.json...), it provides the benefit that:
people reading your source code get an idea of the structure of your program, starting from the less interesting parts and moving up to the business layer
when a test fails, you have some indication of causality (if something failed in 1 - something.js but there are no failures in 0 - base.js then it's probably the fault of the layer covered by 1 - something.js
If you're doing real unit tests of course order should not matter, but I'm rarely able to go with unit tests all the way.
If you prefer a particular order, you can list the files (in order) as command-line arguments to mocha, e.g.:
$ mocha test/test-file-1.js test/test-file-2.js
To avoid a lot of typing every time you want to run it, you could turn this into an npm script in your package.json:
{
// ...
"scripts": {
"test": "mocha test/test-file-1.js test/test-file-2.js"
}
// ...
}
Then run your suite from the command line:
$ npm test
Or if you're using Gulp, you could create a task in your gulpfile.js:
var gulp = require('gulp');
var mocha = require("gulp-mocha");
gulp.task("test", function() {
return gulp.src([
"./test/test-file-1.js",
"./test/test-file-2.js"
])
.pipe(mocha());
});
Then run $ gulp test.
The way it worked for my tests to be executed in a specific order was to create a separate test.js file and then added a describe for each mocha test file I'd wanted to execute.
test.js:
describe('test file 1', function() {
require('./test1.js')
})
describe('test file 2', function() {
require('./test2.js')
})
Then simply run mocha test.js
I am exporting an array with all required files and that is the way I tell mocha the order of execution through index.js file in the folder with all my test files:
const Login = require('../login');
const ChangeBudgetUnit = require('./changeBudgetUnit');
const AddItemsInCart = require('./addItemsInCart');
// if the order matters should export array, not object
module.exports = [
Login,
ChangeBudgetUnit,
AddItemsInCart
];
mocha-steps allows you to write tests that run in a specific sequence, aborting the run at the first failure. It provides a drop-in replacement for it, called steps.
Example usage:
describe('my smoke test', async () => {
step('login', async () => {})
step('buy an item', async () => throw new Error('failed'))
step('check my balance', async () => {})
xstep('temporarily ignored', async () => {})
})
The repo hasn't seen much activity in three years, but it works fine with Mocha 9.

Testing Angular with Gulp-mocha: "Window is not Defined"

I am setting up a project with Gulp to run unit tests with Mocha, including Angular tests. I have the basic set up working (indexOf, etc.), however when I include angular-mocks I get this error or a node-module error:
ReferenceError in 'gulp-mocha': "Window is not defined"
I've tried including angular-module-mocks, using gulp-mocha-phantomjs... but the result is the same. (With mocha-phantomjs my error was 'Init timeout'.) I've seen many examples of configurations with Mocha and Angular or Gulp and Karma but have not yet found a solution for Gulp, Mocha and Angular alone.
I'm thinking of something similar to this Karma solution to correctly load angular-mocks by specifying it in a config file and forcing Gulp to load it (Angular testing with Karma: "module is not defined"). However, even if this would work, it seems like gulp-mocha does not support loading a configuration file (mocha.opts - https://github.com/sindresorhus/gulp-mocha/issues/26). I would be happy to hear a more straightforward solution.
I am using angular-mocks 1.2.22 and gulp-mocha 1.1.0.
Code snippets:
var mocha = require('gulp-mocha');
gulp.task('test', function () {
return gulp.src('test/*.js', {read: false})
.pipe(mocha({reporter: 'nyan', timeout: 400}));
});
test/test.js
var assert = require('assert');
var angular_mocks = require('angular-mocks'); //Fails only when this line is present
//tests
What finally worked for me with Gulp/Browserify/Mocha was using Karma and Mocha combined.
Specifically, I used gulp-karma, and defined the configuration at karma.config.js and used a dummy file for gulp.src as others have done:
gulp.task('test', function () {
return gulp.src('./foobar.js').pipe(karma({
configFile:'karma.config.js',
action: 'run'
}))
.on('error', handleErrors);
});
Then I used this karma.config.js file. I needed the npm modules karma-mocha, karma-chai, and karma-bro. (With only the first two, I was getting 'require is not defined'. Then of course I tried including karma-requirejs, but that does not work with Browserify. Then I tried karma-commonjs, which still didn't work. Then I tried karma-browserify, and got a strange error involving bundle() that no one seems to have solved (https://github.com/xdissent/karma-browserify/issues/46). Karma-bro did the trick.)
I also needed to preprocess each file referenced in the tests as well as the tests themselves. (For using phantomJS also include karma-phantomjs-launcher. And I am using the bower version of angular-mocks simply because it is more recent: v1.2.25 compared to 1.2.22 for npm - but the npm version might work.)
module.exports = function(config) {
config.set({
basePath: '',
// frameworks to use
frameworks: ['browserify', 'mocha', 'chai'],
// list of files / patterns to load in the browser
files: [
'node_modules/angular/lib/angular.min.js',
'bower_components/angular-mocks/angular-mocks.js',
'source/javascript/controllers/*.js',
'source/javascript/*.js',
'test/*.js'
],
reporters: ['progress'],
port: 9876,
colors: true,
autoWatch: true,
browsers: ['PhantomJS'],
preprocessors: {
'source/javascript/controllers/*.js': ['browserify'],
'source/javascript/*.js': ['browserify'],
'test/*.js': ['browserify']
}
});
};
And finally this test passes. At the end I needed to make sure the names of my modules and controllers were consistent (capitals etc.) to resolve 'Module not defined' errors. For debugging I replaced node_modules/angular/lib/angular.min.js with node_modules/angular/lib/angular.js in the files.
describe('Angular', function() {
describe('App Controllers', function() {
beforeEach(angular.mock.module('App'));
describe('MessageCtrl', function() {
it('should retrieve the correct amount of messsages', angular.mock.inject(function($controller) {
var scope = {},
ctrl = $controller('MessageCtrl', {$scope:scope});
assert.equal(scope.messages.length, 2);
}));
});
});
});
I do get this: 'WARNING: Tried to load angular more than once.' I can live with it.

Access "global" mocha.js functions when using require.js

I am including Mocha.js with the excellent use shim for a Require.js-based site.
How do I access the define() and it() BDD functions declared by Mocha when using Require.js?
Here is a basic code example:
test.js:
var mocha = require('use!mocha')
, testFile = require('testFile.js')
mocha.setup('bdd');
mocha.run();
testFile.js:
define(function(require) {
// describe() and it() are not available
describe('Book', function() {
it('should have pages', function() {
});
});
});
I get the error Uncaught ReferenceError: describe is not defined when running in the browser.
I have tried window.describe and tried moving the require('testFile.js') to after the mocha.setup('bdd'). I know I am missing something. Probably passing the context to mocha somehow.
The problem is that the global functions such as describe and it are set up by mocha.setup(). You can use shim config's init property to call mocha.setup() before mocha is exported.
requirejs.config({
shim: {
'mocha': {
init: function () {
this.mocha.setup('bdd');
return this.mocha;
}
}
}
});
require(['mocha', 'test/some_test'], function (mocha) {
mocha.run();
});
Test files need to require mocha.
define(['mocha'], function (mocha) {
describe('Something', function () {
// ...
});
});
Shim config's init property was introduced in RequireJS 2.1. You might be able to use exports property instead of init with RequireJS 2.0.
I found the solution in geddski's amd-testing examples project.
Instead of including the test file(s) at the top along with mocha like so:
define(['use!mocha', 'testFile'],
function(Mocha, TestFile) {
mocha.setup('bdd');
mocha.run();
});
The test file(s) should be included as another require call and mocha.run() embedded in the callback:
define(['use!mocha'],
function(Mocha) {
mocha.setup('bdd');
// Include the test files here and call mocha.run() after.
require(['testFile'],
function(TestFile) {
mocha.run();
});
});

Categories