I thought I'd post this as I stumbled around for a while before noticing what's going on. I have a test suite that uses CouchDB as its logging / recording database. I discovered you can write custom reporters in intern, so thought I could move a lot of my manual 'recordSuccess()'/'recordFailure()' calls out of my test script, and into a custom reporter responding to test pass and fail events.
My main test script still wants to do a little couchdb interaction, so I factored out the couchdb connection and reporting functions into a module, then tried to use that module from both the main test script, and the custom reporter module.
I find that the couchdb helper module is instantiated twice. This goes against the expectation that AMD/RequireJS require() will only execute a module once, and cache the result for use the next time the module is required. If I put a 'debugger' statement in its main body of code, it is clearly executed twice. The upshot, for me, is that the couchdb reference is undefined when called from the reporter.
Directory structure:
runTest.js # helper script to run intern test from this dir
src/MainTest.js
src/CouchHelper.js
src/CouchDBReporter.js
src/intern.js # intern config
runTest.js
node node_modules/.bin/intern-client config=src/intern suites=mypackage/WINTest --envConfig=src/test/dev.json
i.e. MainTest.js:
define([ 'CouchHelper' ], function (CouchHelper) {
.. test startup ..
CouchHelper.connect(username, password, etc);
CouchDBReporter.js:
define([ 'CouchHelper' ], function (CouchHelper) {
return {
'/test/fail': function (test) {
// Presume the couchdb is connected at this point
CouchHelper.recordFailure(test);
}
}
intern.js:
... blah blah ..
loader: {
// Packages that should be registered with the loader in each testing environment
packages: [
'node',
'nedb',
'nodemailer',
{ 'mypackage', 'src' }
],
reporters: [ 'console', 'src/CouchDBReporter' ]
CouchHelper.js:
define([
'intern/dojo/node!node-couchdb'
], function (Couchdb) {
debugger; // this is hit twice
var instance = 0;
function CouchHelper() {
this.couchdb = undefined;
this.instance = instance++;
console.log('Created instance ' + this.instance);
}
CouchHelper.prototype = {
connect: function () { this.couchdb = Couchdb.connect(blah); },
recordFailure: function (test) { this.couchdb.insert(blah); }
}
}
On startup, the console logs:
Created instance 0
Created instance 0
When the reporter calls recordFailure, it calls into a different instance of CouchHelper than the MainTest.js file called connect() on .. so this.couchdb is undefined, and the script crashes. I can call recordSuccess/recordFailure from in MainTest.js just fine, and this.couchdb is valid in CouchHelper, but from the CouchDBReporter the CouchHelper instance is clearly different.
Is this behaviour expected, and if so, what's the recommended way to share data and resources between the main test code, and code in a custom reporter? I see that in 3.0 the reporters config can take an object which might help mitigate this problem, but it feels like one would have to instantiate the reporter programatically rather than define it in config.
Nick
As suggested by Colin, the path to the answer lay in my loader map configuration. This means that my intern.js file, referenced as config
on the command line, has a loader
section where one can define the mappings of paths to AMD module (see https://theintern.github.io/intern/#option-loader). Typically I just define a list of package names, for example I know my test requires nedb, nodemailer, and my own src package:
loader: {
packages: [ 'node', 'nedb', 'nodemailer', 'src' ]
}
For some reason, I had defined my src
package as being available by the name mypackage
:
loader: {
packages: [ 'node', 'nedb', 'nodemailer',
{ name: 'mypackage', location: 'src' }
]
}
I had no good reason to do this. I then specified my custom reporter be loaded by intern using the 'src' package name:
intern.js:
reporters: [ 'console', 'src/CouchDBReporter' ]
And, here's the tricky bit, I referenced my helper module, CouchHelper
, in two different ways, but both times by using a relative module path ./CouchHelper
:
MainTest.js:
require([
'./CouchHelper',
...
], ...
CouchDBReporter.js:
require([
'./CouchHelper',
...
], ...
And on the command line, you guessed it, specified the test to be run as mypackage/MainTest.js
. This conflicts with my specification of src/CouchDBReporter
in intern.js
's reporter
section.
The result was that mypackage/MainTest.js
required ./CouchHelper
which resolved as mypackage/CouchHelper
, and src/CouchDBReporter
required ./CouchHelper
, which resolved as src/CouchHelper
. This loaded the CouchHelper module code twice, working around the usual guarantee with an AMD style loader that a module is only ever loaded once.
It has certainly been a good lesson in AMD module paths, and one implication of using relative paths.