I'm having a big problem with my mocha tests around a global object I'm using. I'm able to produce the following MRE which doesn't give the exact same error, but exemplifies the problematic (buggy?) behavior. Any insight would be much appreciated.
I have the following main.js file in /lib:
exports.exec = function(){
console.log(test);
}
Then the following in /test/test.js:
var should = require('should');
var main = require('../lib/main');
global.test = {something: 1};
describe('normal test', function(){
beforeEach(function(){
global.test = {another: 2};
}),
afterEach(function(){
delete global.test;
});
it ('might work with global', function(){
main.exec();
})
});
Finally, this is test/test2.js:
var should = require('should');
var main = require('../lib/main');
global.test = {third: 3};
describe('some test', function(){
it ('messes up global', function(){
main.exec();
})
});
I expect that the first test would output {another:2} and the second would print {third: 3}. Indeed, this is the behavior I get when I run each test independently. e.g.
jeff#ubuntu:~/workspace/mocha-test$ mocha test/test2.js
{ third: 3 }
․
1 passing (6ms)
However, when running both test with npm packages should and mocha (1.16.1), I get the following output:
jeff#ubuntu:~/workspace/mocha-test$ mocha
{ another: 2 }
․․
1 passing (6ms)
1 failing
1) some test messes up global:
ReferenceError: test is not defined
at Object.exports.exec (/home/jeff/workspace/mocha-test/lib/main.js:3:15)
at Context.<anonymous> (/home/jeff/workspace/mocha-test/test/test2.js:8:10)
at Test.Runnable.run (/usr/lib/node_modules/mocha/lib/runnable.js:211:32)
at Runner.runTest (/usr/lib/node_modules/mocha/lib/runner.js:355:10)
at /usr/lib/node_modules/mocha/lib/runner.js:401:12
at next (/usr/lib/node_modules/mocha/lib/runner.js:281:14)
at /usr/lib/node_modules/mocha/lib/runner.js:290:7
at next (/usr/lib/node_modules/mocha/lib/runner.js:234:23)
at Object._onImmediate (/usr/lib/node_modules/mocha/lib/runner.js:258:5)
at processImmediate [as _immediateCallback] (timers.js:330:15)
test2.js should be structured like this:
describe('some test', function(){
before(function (){
global.test = {third: 3};
});
it ('messes up global', function(){
main.exec();
})
});
travisjeffery on the GitHub issue mentioned in the comment explains:
mocha loads the files and then runs the suites, to reliably setup your tests the setup should be within the suite.
As #SB points out, this may not be amenable to sharing things like global variables across tests.
Related
We have an SPA application with a shared scope variable var WIDGET. Each module adds itself to the shared WIDGET object or creates the WIDGET if it was not created.
Using a couple of frameworks for unit tests the shared WIDGET is available but the variable on the required files do not point to it.
var WIDGET = WIDGET || {};
if I change this line of code to WIDGET = WIDGET || {} or if I remove it all together then the objects will work as expected in the unit test
things to know:
we do NOT use nodejs in production, it is just for testing
code executes properly in production
test frameworks have been mocha and jest
Q1 what can I do to ensure the private variable is pointing to the shared object when unit testing?
Q2 why does this happen in testing but not in the DOM during application execution?
test application creation:
TypeError: Cannot read property 'getInstance' of undefined
at new widget_application (widgetApplication.js:23:41)
at Object.getInstance (widgetApplication.js:33:16)
at Context.<anonymous> (test\testApp.js:16:39)
first module that adds itself to the WIDGET object
var _WIDGET_ = _WIDGET_ || {};
//adds itself to the _WIDGET_ to be shared
_WIDGET_.widgetLogger = (function(){
var INSTANCE;
var _config = {};
var _widgetLog = {};
function widget_logger(config){
if(config){_config = config;}
}
widget_logger.prototype.stageLog = stageLog;
// widget_logger.prototype.startUpLog = startUpLog;
// widget_logger.prototype.dataRefreshLog = dataRefreshLog;
// widget_logger.prototype.widgetNotReadyLog = widgetNotReadyLog;
// widget_logger.prototype.finalizeLog = finalizeLog;
// widget_logger.prototype.getLog = getLog;
return {
getInstance:function(c){
if(!INSTANCE){
INSTANCE = new widget_logger(c);
}
return INSTANCE;
}
};
function stageLog(data) {
console.log("widget logger called to stage log");
if(data){
_widgetLog = {};
_legacyLog = {};
_widgetLog.widgetProgramCode = _config.versionData.programCode;
_widgetLog.widgetVersion = _config.versionData.version;
_widgetLog.startTime = data.startTime || new Date();
console.log("widget logger staged this log ",_widgetLog);
}
}
})();
//export is only for testing - nodejs NOT used in production
var module = module || {};
module.exports = _WIDGET_;
next module that adds itself and uses the logger module
var _WIDGET_ = _WIDGET_ || {};//remove this line of code and unit test run
var _API_ = _API_ || {};
_WIDGET_.smallApplication = (function(){
var INSTANCE;
var widgetLogger;
var _config = {
versionData:{
programCode:"smallApplication",
version:"1.0.2.0"
}
};
function widget_application(config){
console.log("make the instance ");
// var elem = document.getElementById('apprunning');
// elem.innerHTML += " smallApplication is online ";
if(config){_config = config;}
//expects to have a shared object already
//fails after this call because this module create a new _WIDGET_ variable
//this does not happen in production - all components are created
widgetLogger = _WIDGET_.widgetLogger.getInstance(_config);
}
widget_application.prototype.runApplication = runApplication;
return {
getInstance:function(c){
console.log("get instance was called");
if(!INSTANCE){
INSTANCE = new widget_application(c);
}
return INSTANCE;
}
};
//run some functions and hello world and all that happiness...
function runApplication(){
console.log("widget application runs - will call logger");
widgetLogger.stageLog(true);
}
})();
//export is only for testing - nodejs NOT used in production
var module = module || {};
module.exports = _WIDGET_;
mocha test script
var assert = require('assert');
_WIDGET_ = {name: 'small application'};
describe('small application',function(){
//_WIDGET_ = {name: 'small application'};
//var _WIDGET_ {name: 'small application'};
//global._WIDGET_ {name: 'small application'};
it('test logger creation',function(){
_WIDGET_ = require('../widgetLogger');
console.log('_WIDGET_');
});
it('test application creation',function(){
var app = require('../widgetApplication');
_WIDGET_ = require('../widgetApplication');
});
});
A2 to Q2 found at free code camp - require modules In a browser, when we declare a variable in a script like this:
var answer = 42;
That answer variable will be globally available in all scripts after the script that defined it.
This is not the case in Node. When we define a variable in one module, the other modules in the program will not have access to that variable. So how come variables in Node are magically scoped?
The answer is simple. Before compiling a module, Node wraps the module code in a function, which we can inspect using the wrapper property of the module module.
Big Thanks to free code camp
They made it simple to see that node.js require('module') wraps the module in a function().
That helped me to realize I needed to concat my modules into 1 file for testing. Then require('minifiedModules')
in my unit tests the code looks like
var _Widget_ = require('minifiedModules');
var app;
test('verify _Widget_',function(){
//testing with jest
expect(_Widget_.widgetLogger).toBeDefined();
expect(_Widget_.smallApplication).toBeDefined();
});
test('instantiate app',function(){
app = _Widget_.smallApplication.getInstance();
});
test('run app',function(){
app.runApplication();
});
and running npm test now works
> jest
PASS ./app.test.js
√ _WIDGET_ objects (34ms)
√ instantiate app (5ms)
√ run app (5ms)
console.log app.test.js:23
Widget objects { widgetLogger: { getInstance: [Function: getInstance] },
smallApplication: { getInstance: [Function: getInstance] } }
console.log test/scripts/services/services.min.js:87
get instance was called
console.log test/scripts/services/services.min.js:71
make the instance
console.log test/scripts/services/services.min.js:78
no way this works, _WIDGET_, { widgetLogger: { getInstance: [Function: getInstance] },
smallApplication: { getInstance: [Function: getInstance] } }
console.log test/scripts/services/services.min.js:97
widget application runs - will call logger
console.log test/scripts/services/services.min.js:30
widget logger called to stage log
console.log test/scripts/services/services.min.js:40
widget logger staged this log { widgetProgramCode: 'smallApplication',
widgetVersion: '1.0.2.0',
startTime: 2018-10-30T03:41:10.815Z }
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 2.067s
Ran all test suites.
Just starting with Sinon, Mocha, and Chai JS. After upgrading UnderscoreJS version from 1.4.4 to 1.9.1, I had to update how my project was using template function.
Previously, _.template function was used in this way -
var myTemplate = _.template("<p><%= name %></p>", {name: 'Joe Doe'});
New Way,
// `myTemplate` here is a function!
var myTemplate = _.template("<p><%= name %></p>");
// Now let's pass in the data for the template.
myTemplate({name: 'Joe Doe'}); // it returns: "<p>Joe Doe</p>"
However, this change caused a lot of the existing test cases to fail. Below mentioned is one of the test cases I need help with -
const sinonVar = require('sinon');
describe('testVar', function() {
var sanboxVar;
var ss = requirejs('Path to JS library');
var testVar;
beforeEach(function(done) {
console.log("Not Called"); // Never printed on console
done();
sanboxVar = sinonVar.sanbox.create();
});
it('some text here', function() {
console.log("sanboxVar: " + sanboxVar); //Printed on console as 'undefined'
ss.id = sanboxVar.stub();
});
});
On "npm test", I see the error -
testVar
some text here:
TypeError: Cannot read property 'stub' of undefined
at Context.<anonymous> (testPage.js) - "This is pointing to sanboxVar not defined"
I think for some reason, beforeEach method is not getting called and hence the Sandoval variable is not getting initiated.
Any help would be appreciated.
I am writing some utilities that I intend to reuse across my JavaScript. This is also a learning exercise on my part. In an attempt to do this, I have the following file:
utilities.js
Object.prototype.AddPrefix = function(prefix) {
return prefix + this;
};
I intend to call this function in JavaScript like this:
var myString = 'agree';
myString = myString.AddPrefix('dis');
Please note, that is a contrived example. I'm just trying to demonstrate calling the function. Either way, I want to test the AddPrefix function. To do that, I'm using Jasmine. I have the following Jasmine file:
utilities.tests.js
'use strict';
describe('utilities', function() {
describe('add prefix', function() {
it('append dis to string', function() {
var v = 'agree';
var actual = v.AddPrefix('dis');
var expected = 'disagree';
expect(actual).toBe(expected);
});
});
});
The two files, utilities.js and utilities.tests.js are in the same directory. I am executing Jasmine via gulp with the following script:
gulpfile.js
var gulp = require('gulp');
var jasmine = require('gulp-jasmine');
gulp.task('default', function() {});
gulp.task('test', function() {
var testFiles = [
'utilities/utilities.tests.js'
];
return gulp
.src(testFiles)
.pipe(jasmine());
});
When I execute gulp test from the command-line, I get the following error:
[09:44:33] Using gulpfile C:\Projects\test\gulpfile.js
[09:44:33] Starting 'test'...
F
Failures:
1) utilities add prefix
1.1) TypeError: Object agree has no method 'AddPrefix'
1 spec, 1 failure
Finished in 0 seconds
[09:44:33] 'test' errored after 42 ms
[09:44:33] Error in plugin 'gulp-jasmine'
Message:
Tests failed
Its like utilities.tests.js does not know where utilities.js is located. However, I do not know how to reference it. How do I do this?
Your utilities.js file is never being executed. You need to require('./utilities') in utilities.tests.js:
'use strict';
require('./utilities');
describe('utilities', function() {
describe('add prefix', function() {
it('append dis to string', function() {
var v = 'agree';
var actual = v.AddPrefix('dis');
var expected = 'disagree';
expect(actual).toBe(expected);
});
});
});
I can get tests to run in Intern, but I am struggling with getting spies to work. I'm trying to integrate sinon so I can get spies. Here is a sample test file:
define([
'intern!bdd',
//'intern/chai!expect',
//'intern/order!node_modules/intern/chai',
// 'intern/order!node_modules/chai/lib/chai',
// 'intern/order!node_modules/sinon/lib/sinon',
// 'intern/order!node_modules/sinon-chai/lib/sinon-chai',
'intern/order!node_modules/sinon/lib/sinon',
'intern/order!node_modules/sinon/lib/sinon/spy',
'intern/order!node_modules/sinon/lib/sinon/call',
'intern/order!node_modules/sinon/lib/sinon/behavior',
'intern/order!node_modules/sinon/lib/sinon/stub',
'intern/order!node_modules/sinon/lib/sinon/mock',
'intern/order!node_modules/sinon/lib/sinon/collection',
'intern/order!node_modules/sinon/lib/sinon/assert',
'intern/order!node_modules/sinon/lib/sinon/sandbox',
'intern/order!node_modules/sinon/lib/sinon/test',
'intern/order!node_modules/sinon/lib/sinon/test_case',
'intern/order!node_modules/sinon/lib/sinon/match',
'intern/order!vendor/src/angular/angular',
'intern/order!vendor/src/angular-mocks/angular-mocks',
'intern/order!src/common/modules/error_handling/error_handling'
], function (bdd, sinon, spy, call, behavior, stub, mock, collection, assert, sandbox, test, test_case, match) {
with (bdd) {
sinon.spy = spy;
sinon.call = call;
sinon.behavior = behavior;
sinon.stub = stub;
sinon.mock = mock;
sinon.collection = collection;
sinon.assert = assert;
sinon.sandbox = sandbox;
sinon.test = test;
sinon.test_case = test_case;
sinon.match = match;
describe('Error handler module', function () {
var test, scope, ctrl, error_handler, log;
function inject (fn) {
return function() {
angular.injector(['ng', 'ngMock', 'error_handling']).invoke(fn);
}
}
beforeEach(inject(function($log){
log = $log;
}));
it('should be an object', function(){
//expect(log).to.be.an('object');
});
it('should call console.trace with the string test', function(){
var spy = sinon.spy(console.trace);
//expect(sinon.spy).to.be.ok;
//log.debug('test');
console.trace('test');
//spy.should.be.ok;
spy.should.have.been.calledWith('test');
//chai.expect(spy).to.have.been.called.with('test');
});
});
}
});
I based it off of https://github.com/theintern/intern/blob/sinon/sinon.js
But I get this error failure:
>> 1/6 tests failed
Warning: FAIL: main - Error handler module - should log nothing when the logging mode is off (1ms)
TypeError: 'undefined' is not an object (evaluating 'spy.should.have')
at </Users/evanvandegriff/Documents/work/nomi_v2/nomi_v2/web/src/common/modules/error_handling/error_handling.test.js:67>
at <__intern/lib/Test.js:169>
at <__intern/lib/Suite.js:237>
at <__intern/node_modules/dojo/Deferred.js:37>
at <__intern/node_modules/dojo/Deferred.js:258>
at runTest <__intern/lib/Suite.js:241>
at <__intern/lib/Suite.js:249
Anyone have any ideas why this isn't working?
As #Fordio indicated, the syntax you're trying to use is part of Sinon-Chai, not vanilla Sinon.js. You'll need to require that package as a dependency, or use the native Sinon.js assertions.
I get an error when running Jasmine tests: 'ReferenceError: JSZip is not defined'. Here's my controller:
$scope.makeZip = function() {
var zip = new JSZip();
zip.file('myPhoto.txt', 'Hello World);
return 'foo' + zip.generate();
};
And test:
it('should make a zip with correct name', function() {
var zipFileName = scope.makeZip();
expect(zipFileName).toMatch('foo');
});
My guess is that I need to mock JSZip constructor somehow: I tried inserting the following at the beginning of the test:
spyOn(window, 'JSZip').andReturn(null);
but then I get 'JSZip() method does not exist'.
I thought this is similar problem to Jasmine - How to spy on a function call within a function?, but I couldn't find a right way to fix the issue.
Any ideas? Thanks for all help!
JSZip is defined on the window. You should not have to pass it into the constructor. In your jasmine page (where you holster your specs), reference jszip.js. If you want, you could create a service..
your angular module.value("JSZip",JSZip);
then pass it into your controller. This is only useful if you wanted to mock JSZip, though.
Solved it! I didn't actually want to test JSZip, only to check whether a function makeZip is called correctly.
it('should make a zip with correct name', function() {
window.JSZip = function() {
this.file = function() {};
this.generate = function() {
return 'bar';
};
};
var zipFileName = scope.makeZip();
expect(zipFileName).toMatch('foo'); //makeZip should return string containing 'foo'
});