I'm trying to use Mocha to test a CLI app. The tests are running fine but, when I launch the testing procedure, it also launches the main app:
$ npm run test
> standardize-js#0.2.2 test C:\Users\Gaspard\Documents\Code\standardize-js
> mocha "./source/**/*.spec.js"
? Choose your project language or framework (Use arrow keys) //<-- THIS IS THE PROGRAM
> Javascript
Typescript
AngularJS
Main function //<-- THIS IS THE TEST
ask if the configuration is valid
Configuration is not valid, terminating program.
√ should return false if the configuration is not accepted
1 passing (29ms)
I'm kind of new to the testing world and I'm really struggling to understand what I'm doing wrong.
Here is the NPM script used to launch mocha :
"test": "mocha \"./source/**/*.spec.js\""
Here is my testing method:
/* eslint-disable func-names */
const { expect } = require("chai");
const main = require("./index").test;
describe("Main function", function() {
describe("ask if the configuration is valid", function() {
it("should return false if the configuration is not accepted", function() {
const fakeAnswer = false;
expect(main.validateConfiguration(fakeAnswer)).to.equal(false);
});
});
});
And here is my index.js file:
function validateConfiguration(answer) {
if (answer === false) {
console.log(chalk.red("Configuration is not valid, terminating program."));
return false;
}
return true;
}
const run = async () => {
//MAIN FUNCTION
};
run();
// Export functions and variables to be able to test
exports.test = {
validateConfiguration
};
It's not a problem with mocha. It is simply now node.js modules work.
When you do this:
const main = require("./index").test;
Node.js will execute index.js and then check the value of module.exports. If the module (index.js) sets or modifies module.exports then node will export it for use by require(). But note, in order for node to know that the module has exported anything it must execute the javascript file.
Node.js does not have any ability to parse and analyze javascript syntax (that's V8's job). Unlike other languages such as C or Java, modules in node.js are not implemented at the syntax level. Therefore the javascript language does not need to be modified (eg. ES6 modules) for node.js to support modules. Modules are simply implemented as a design pattern.
In your index.js file you call run:
run();
When require() loads index.js it will therefore also cause run() to be called.
Test libraries, not main
The solution to this is to implement your own logic as modules and test that, not test index.js:
mylib.js:
function validateConfiguration(answer) {
if (answer === false) {
console.log(chalk.red("Configuration is not valid, terminating program."));
return false;
}
return true;
}
// Export functions and variables to be able to test
exports.test = { validateConfiguration };
index.js:
const validateConfiguration = require("./mylib").test;
const run = async () => {
//MAIN FUNCTION
};
run();
You can now use your test script as written.
How can you not test code??
The strategy to keep index.js bug free without testing is to remove all logic from it except for the minimum amount of code to wire all your other code up together to run the app. The code should be as simple as "Hello World". That way, the code in main is so small and so simple that you can test it for bugs using your eyeballs.
Any code in index.js that causes a bug should be refactored into its own library so that it can be tested separately. There are a small handful of corner cases, such as loading environment variables or opening port 80 where you can't really separate into a library because they literally are wiring logic. For such cases you just have to be really careful.
It's calling run because you are telling it to right after defining the method.
Related
I have a module that exports a method used in JEST test. I want to run it from command line too.
async function doRun() { /* do something */}
exports.doRun = doRun;
This works well from JEST, where I import the module and execute the method. But when I call it with node module.js, it has no effect because the method is not executed within JS body. To fix it I have to add:
doRun().then(() => console.log('finished'));
which makes the code work from CLI but it is executed too in JEST just after import.
Am I right that I need to create new file that imports the module and runs the code just for CLI?
const module = require('module.js')
doRun().then(() => console.log('finished'));
and then run node module_cli.js?
Pretty much, yeah. :-)
Either that, or use an environment variable
async function doRun() { /* do something */}
exports.doRun = doRun;
if (process.env.AUTO_RUN === "Y") {
doRun().then(() => console.log('finished'));
}
and run it like this on *nix:
AUTO_RUN=Y node module.js
...or on Windows according to this it would be:
cmd /V /C "set AUTO_RUN=Y&&node module.js"
You could also use the standard NODE_ENV environment variable. I haven't gotten deep into Jest, but it might set it to "testing" or something like that...
I'm using babel to enable ES6 imports in a node project. Also using mocha for testing, and istanbul for coverage. I end up with less than full coverage because babel generates code something like the following:
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _koa = require('koa');
var _koa2 = _interopRequireDefault(_koa);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
Specifically, the generated function _interopRequireDefault is copied into every code file, and the branches are not necessarily always executed, which skews the branch coverage number emitted for istanbul. Is there any way around this issue?
If you're using gulp, I have a gist with a gulpfile here that sets up the necessary hooks and filters. The relevant chunk is to load isparta, hook require, and let the tests run:
gulp.task('test:cover', (cb) => {
gulp.src('src/main/**/*.js')
.pipe(istanbul({
instrumenter: require('isparta').Instrumenter,
includeUntested: true
}))
.pipe(babel())
.pipe(gulp.dest('target/cover'))
.pipe(istanbul.hookRequire())
.on('finish', cb);
});
gulp.task('test:mocha', (cb) => {
gulp.src('target/test/**/Test*')
.pipe(mocha())
.pipe(istanbul.writeReports())
.on('end', cb);
});
gulp.task('test', (cb) => {
return runSequence('test:cover', 'test:mocha', cb);
});
The only frustrating part is that your tests must use the covered code:
import {
LinearInterpolator,
CosineInterpolator
} from '../../cover/random/Interpolators';
I haven't found a way to work around that yet without also covering the test scripts and skewing coverage, although you should be able to do that by merging streams:
gulp.task('test:cover', (cb) => {
const src = gulp.src('src/main/**/*.js')
.pipe(istanbul({
instrumenter: require('isparta').Instrumenter,
includeUntested: true
}));
const test = gulp.src('src/test/**/*.js');
merge(src, test)
.pipe(babel())
.pipe(gulp.dest('target/cover'))
.pipe(istanbul.hookRequire())
.on('finish', cb);
});
You need to combine it with isparta - https://github.com/douglasduteil/isparta - to get the coverage working correctly. I warn you its a bit trial and error at the moment! My npm script looks like -
"coverage": "node_modules/.bin/babel-node node_modules/.bin/isparta cover --include-all-sources --report html node_modules/.bin/_mocha -- --reporter $npm_package_config_bdd_reporter",
We've run into this and I finally got fed up and looked into what causes this line. It turns out that every time to use an import like:
import chai from 'chai';
this babel fill gets added to allow sane interaction with older export styles. The trouble is that none of the common libraries exhibit the "true" branch of the ternary. I build the following file coverInterrop.js that artificially trips the first branch using old-school exports:
module.exports = {
__esModule: true
};
and I include in any file where I want to use an undestructured import:
// eslint-disable-next-line no-unused-vars
import coverInterrop from 'coverInterrop';
Note that it has to assign to a variable to trip the coverage and good eslint rules won't like that
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");
I would like to unit test my JavaScript solution for a challenge on the CodeEval site using Mocha framework.
I could create separate files or use some other fancy approach, but:
I want my solution to be ready to upload just after unit tests pass, without any files changes (that I could forgot to do).
Additional code should not introduce unnecessary time or memory usage.
But CodeEval requirements and Mocha seem incompatible together, or not?
Default CodeEval stub:
var fs = require("fs");
fs.readFileSync(process.argv[2]).toString().split('\n').forEach(function (line) {
if (line != "") {
//do something here
console.log(answer_line);
}
It's quite simple to check numbers of parameters of the program. With CodeEval it will be 3, with mocha it will be 2. Mocha execution always falls into "else" and exports the logic function.
Tested solution.
var fs = require("fs");
module.exports = processLine;
if (process.argv.length === 3) {
// codeeval mode
fs.readFileSync(process.argv[2]).toString().split('\n').forEach(function (line) {
if (line != "") {
console.log(processLine(line));
}
});
}
function processLine(line) {
return line;
}
Remarks:
It won't work if mocha is run with parameters nor with mocha.opts file, unless there's more then 3 parameters;
setting module.exports inside the files doesn't bother CodeEval.
I am currently using requirejs to manage module js/css dependencies.
I'd like to discover the possibilities of having node do this via a centralized config file.
So instead of manually doing something like
define([
'jquery'
'lib/somelib'
'views/someview']
within each module.
I'd have node inject the dependencies ie
require('moduleA').setDeps('jquery','lib/somelib','views/someview')
Anyway, I'm interested in any projects looking at dependency injection for node.
thanks
I've come up with a solution for dependency injection. It's called injectr, and it uses node's vm library and replaces the default functionality of require when including a file.
So in your tests, instead of require('libToTest'), use injectr('libToTest' { 'libToMock' : myMock });. I wanted to make the interface as straightforward as possible, with no need to alter the code being tested. I think it works quite well.
It's just worth noting that injectr files are relative to the working directory, unlike require which is relative to the current file, but that shouldn't matter because it's only used in tests.
I've previously toyed with the idea of providing an alternate require to make a form of dependency injection available in Node.js.
Module code
For example, suppose you have following statements in code.js:
fs = require('fs');
console.log(fs.readFileSync('text.txt', 'utf-8'));
If you run this code with node code.js, then it will print out the contents of text.txt.
Injector code
However, suppose you have a test module that wants to abstract away the file system.
Your test file test.js could then look like this:
var origRequire = global.require;
global.require = dependencyLookup;
require('./code.js');
function dependencyLookup (file) {
switch (file) {
case 'fs': return { readFileSync: function () { return "test contents"; } };
default: return origRequire(file);
}
}
If you now run node test.js, it will print out "test contents", even though it includes code.js.
I've also written a module to accomplish this, it's called rewire. Just use npm install rewire and then:
var rewire = require("rewire"),
myModule = rewire("./path/to/myModule.js"); // exactly like require()
// Your module will now export a special setter and getter for private variables.
myModule.__set__("myPrivateVar", 123);
myModule.__get__("myPrivateVar"); // = 123
// This allows you to mock almost everything within the module e.g. the fs-module.
// Just pass the variable name as first parameter and your mock as second.
myModule.__set__("fs", {
readFile: function (path, encoding, cb) {
cb(null, "Success!");
}
});
myModule.readSomethingFromFileSystem(function (err, data) {
console.log(data); // = Success!
});
I've been inspired by Nathan MacInnes's injectr but used a different approach. I don't use vm to eval the test-module, in fact I use node's own require. This way your module behaves exactly like using require() (except your modifications). Also debugging is fully supported.