Reputation: 303
Vague version question:
Context:
I'm working on an existent node project that has no tests yet, so I read some and realized to use karma and jasmine.
I read some tutos (including these):
So I'm trying to run my specs with grunt and getting this error:
X encountered a declaration exception ReferenceError: Can't find variable: require in file:///(...)-spec.js (line 2) (1)
The line is something like:
var myHelper = require(...);
But if I use via terminal "node-jasmine test" it works like a charm...
My project structure:
- controllers/
- helpers/
- models/
- node_modules/
- resources/
- test/
- test/spec/
- views/
- app.js
- Gruntfile.js
- package.json
In my spec (inside test/spec/) I use a require('../../helpers/helper.js') and that's ok for node-jasmine, but no with grunt.
node-jasmine test:
.....
Finished in 0.015 seconds 5 tests, 5 assertions, 0 failures, 0 skipped
grunt:
Running "jasmine:pivotal" (jasmine) task Testing jasmine specs via PhantomJS
ReferenceError: Can't find variable: require at app.js:1 Service Helper Tests X encountered a declaration exception ReferenceError: Can't find variable: require in file:///(...)/test/spec/serviceHelper-spec.js (line 2) (1)
1 spec in 0.005s.
1 failures Warning: Task "jasmine:pivotal" failed. Use --force to continue.
Aborted due to warnings.
I have all packages installed into node_modules (nothing in dependencies in package.json) and my Gruntfile.js is:
'use strict';
module.exports = function(grunt) {
var $srcFiles = 'app.js';
var $testFiles = 'test/spec/*-spec.js';
var $outputDir = 'test/target'
var $junitResults = $outputDir + '/junit-test-results.xml';
var $jasmineSpecRunner = $outputDir + '/_SpecRunner.html';
var $coverageOutputDir = $outputDir + '/coverage';
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// Jasmine test
jasmine: {
pivotal: {
src: $srcFiles,
options: {
specs: $testFiles,
outfile: $jasmineSpecRunner,
keepRunner: 'true' // keep SpecRunner/outfile file
}
}
},
// coverage using Karma
karma: {
continuous: {
singleRun: 'true',
browsers: [ 'PhantomJS' ]
},
options: {
plugins: [
'karma-jasmine',
'karma-phantomjs-launcher',
'karma-junit-reporter',
'karma-coverage'
],
frameworks: [ 'jasmine' ],
files: [ $srcFiles, $testFiles ],
reporters: [ 'junit', 'coverage' ],
junitReporter: {
outputFile: $junitResults
},
preprocessors: {
// source files must be a literal string
'helpers/*.js': [ 'coverage' ]
},
coverageReporter: {
type: 'lcov',
dir: $coverageOutputDir
}
}
},
// export Karma coverage to SonarQube
karma_sonar: {
your_target: {
// properties for SonarQube dashboard
project: {
key: 'net.ahexample:ahexample-jasmine-karma-sonar',
name: 'Jasmine with Karma and SonarQube Example',
version: '0.0.1'
}
// sources property is set at runtime (see below)
}
},
clean: [ $outputDir ]
});
/*
* Task to set karma_sonar's sources property.
* This is needed because karma (coverage) stores its results in a
* directory whose name uses the browser's user agent info
* (name/version and the platform name).
* The latter may well he different to the OS name and so its needs an
* OS to platform translator.
* For example, OS name for Apple Mac OS X is Darwin.
*/
grunt.registerTask('set-karma-sonar-sources-property', function() {
var $done = this.async();
var $phantomjs = require('karma-phantomjs-launcher/node_modules/phantomjs');
var $spawn = require('child_process').spawn;
var $phantomUserAgent = $spawn($phantomjs.path,
// phantomjs script to print user agent string
[ 'lib/phantomjs-useragent.js' ]
);
/*
* Construct coverage LCOV file path from PhantomJS'
* user agent string, then use it to set karma_sonar's
* sources property.
*/
$phantomUserAgent.stdout.on('data', function(msg) {
var $useragent = require('karma/node_modules/useragent');
var $agent = $useragent.parse(msg);
// An example of dirName is 'PhantomJS 1.9.7 (Mac OS X)'
var $dirName = $agent.toAgent() + ' (' + $agent.os + ')';
var $coverageResults = $coverageOutputDir + '/' + $dirName + '/lcov.info';
var $sonarSources = makeSonarSourceDirs($srcFiles, $coverageResults);
var $karmaSonarConfig = 'karma_sonar';
var $ksConfig = grunt.config($karmaSonarConfig);
grunt.log.writeln('coverage LCOV file: ' + $coverageResults);
$ksConfig['your_target']['sources'] = $sonarSources;
grunt.config($karmaSonarConfig, $ksConfig);
});
$phantomUserAgent.on('close', function(exitCode) {
$done();
});
/*
* Create sonar source object for each directory of source file pattern.
*/
function makeSonarSourceDirs($filesPattern, $coverageResults) {
var $path = require('path');
var $dirs = [];
grunt.file.expand(
{
filter: function($filePath) {
$dirs.push({
path: $path.dirname($filePath),
prefix: '.', // path prefix in lcov.info
coverageReport: $coverageResults,
testReport: $junitResults
});
}
},
$filesPattern
);
return $dirs;
}
});
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-jasmine');
grunt.loadNpmTasks('grunt-karma');
grunt.loadNpmTasks('grunt-karma-sonar');
grunt.registerTask('test', [ 'jasmine', 'karma:continuous' ]);
grunt.registerTask('sonar-only', [ 'set-karma-sonar-sources-property', 'karma_sonar' ]);
grunt.registerTask('sonar', [ 'test', 'sonar-only' ]);
grunt.registerTask('default', 'test');
}
Thank you for your attention.
Upvotes: 5
Views: 3928
Reputation: 195
It depends:
If you have some application code that you need to test against a browser (e.g. Angular
, Backbone
, etc.) - Use Karma
and don't use require
. Then just make sure your helpers.js
file loads before the tests themselves.
// @file Gruntfile.js
// https://github.com/karma-runner/grunt-karma
grunt.initConfig({
karma: {
client: {
options: {
files: ['client/*.js', 'helpers/*.js', 'test/*.js']
}
}
}
});
// @file helpers.js
(function () {
window.helpers = {
foo: function () {
return 'bar';
}
};
})();
// @file spec.js
(function (helpers) {
it('does the thing', function () {
expect(helpers.foo()).toBe('bar');
});
})(window.helpers);
If you don't need to run your tests against a browser (i.e. you are strictly testing NodeJS
code), you can simplify your setup by removing Karma
and strictly using Jasmine
:
// @file Gruntfile.js
// https://github.com/gruntjs/grunt-contrib-jasmine
grunt.initConfig({
jasmine: {
server: {
src: 'server/*.js',
options: {
specs: 'test/*.js',
helpers: 'helpers/*.js'
}
}
}
});
// @file helpers.js
(function () {
module.exports = {
foo: function () {
return 'bar';
}
};
})();
// @file spec.js
(function () {
var helpers = require('helpers'); // require is available
it('does the thing', function () {
expect(helpers.foo()).toBe('bar');
});
})();
require
doesn't exist because you are using Karma
to run your tests. Karma
simply loads files in a browser of your choosing and executes them in the order you provide in your karma.conf.js
. It internally uses the test framework you provide (in this case Jasmine
) to run tests against the browser(s) you provide (in this case PhantomJS
).
As is true with all JavaScript, the variable context is defined by the closure it is contained within.
The Jasmine
binary internally uses NodeJS
, which emulates CommonJS require, making the require
function available to you within the context of your node application.
The Karma
runner does what is the equivalent of writing <script src="[path]">
tags to the browser, which then each load the corresponding file into PhantomJs
. As a result, your javascript context is global, and your files only have access to the global context. In a browser, the global context is defined by everything attached to the window
object, and window.require
does not inherently exist.
Upvotes: 5