I have a problem and can't seem to find the solution.
I'm writing Node.js code and using Sinon to unittest. I am testing a service and stubbing calls to the repository/database. Everything works despite for one repository method where the actual method is invoked instead of its stub. Here's the relevant code.
listingServiceTest.js
var chai = require('chai');
var expect = chai.expect;
var sinon = require('sinon');
var listingService = require('../listingService');
var listingRepository = require('../../repository/listingRepository');
var Listing = require('../../models/interfaceModel/listing');
//some testdata initialization
[..]
describe('saveListing(listing)', function() {
//everything works fine in here
[...]
});
describe('saveTags(listingID, tags)', function() {
beforeEach(function() {
sinon.stub(listingRepository, 'findTags', function(tags, callback) {
//This one works fine
var tags = [{
text: tags[0],
tagID: tags[0] + 'ID'
}];
setTimeout(function () {
callback (tags);
}, 10);
});
sinon.stub(listingRepository, 'saveTagCorrelation', function(listingID, tagID, done) {
setTimeout(function() {
//Here The actual Method is called instead of the stub ?!?!?
console.log('im saveTagCorrelation stub');
done();
}, 10);
});
sinon.stub(listingRepository, 'saveTag', function(tag, callback) {
//This one works fine
});
});
afterEach(function() {
listingRepository.findTags.restore();
listingRepository.saveTag.restore();
listingRepository.saveTagCorrelation.restore();
});
it("saveTags should execute this callback", function() {
listingService.saveTags(util.slugify(pTitle), pTags, function() {
//This obviously fails since the the stub isn't called
expect(listingRepository.saveTagCorrelation.getCall(0).args[0]).to.equal(util.slugify(pTitle));
expect(listingRepository.saveTagCorrelation.getCall(0).args[1]).to.equal(pTags[0] + 'ID');
expect(listingRepository.saveTag.getCall(0).args[0]).to.equal(pTags[1]);
expect(listingRepository.saveTagCorrelation.getCall(1).args[0]).to.equal(util.slugify(pTitle));
expect(listingRepository.saveTagCorrelation.getCall(1).args[1]).to.equal(pTags[1] + 'ID');
});
});
});
And here's the repo, that is supposed to be stubbed (I can see in the logs, that the actual method is logging, instead of the stub).
listingRepository.js:
var db = require ('../models/index');
var util = require ('../util');
module.exports = {
saveListing: [...],
findTags: [...],
saveTag: [...],
saveTagCorrelation: function (listingID, tagID){
//This is called instead of the stub
console.log('in actual Method');
var tagToSave;
tagToSave = db.tag_listing.build({
listingID: listingID,
tagID: tagID
});
tagToSave.save();
},
getListingByID: [...]
}
Am I blind? What am I missing?
Background
I am working on a program in Node.js and writing my test suites in Mocha with Chai and SinonJS. I have core graphics module which controls access to a node-webgl context.
Due to how node-webgl works, I only wish to initialize a context once for the entire test run. I have some tests I wish to run prior to the initialization of the core module, like so:
describe('module:core', function () {
describe('pre-init', function () {
describe('.isInitialized', function () {
it('should return false if the module is not initialized', function () {
expect(core.isInitialized()).to.be.false;
});
});
describe('.getContext', function () {
it('should error if no context is available', function () {
expect(function () {
core.getContext();
}).to.throw(/no context/i);
});
});
});
describe('.init', function () {
it('should error on an invalid canvas', function () {
expect(function () {
core.init(null);
}).to.throw(/undefined or not an object/i);
expect(function () {
core.init({});
}).to.throw(/missing getcontext/i);
});
it('should error if the native context could not be created', function () {
var stub = sinon.stub(global._canvas, 'getContext').returns(null);
expect(function () {
core.init(global._canvas);
}).to.throw(/returned null/i);
stub.restore();
});
it('should initialize the core module', function () {
expect(function () {
core.init(global._canvas);
}).not.to.throw();
});
});
describe('post-init', function () {
describe('.isInitialized', function () {
it('should return true if the module is initialized', function () {
expect(core.isInitialized()).to.be.true;
});
});
describe('.getContext', function () {
it('should return the current WebGL context', function () {
var gl = null;
expect(function () {
gl = core.getContext();
}).not.to.throw();
// TODO Figure out if it's actually a WebGL context.
expect(gl).to.exist;
});
});
});
});
Then I can run the remaining tests.
Problem
When I run this through Mocha, everything is fine since the core test suite is the first thing to be run. My concern is that if any test suites get run before the core test suite, then those test suites will fail as the core is not initialized yet.
What is the best way to ensure the core test suite is always run before any other test suites?
In the end I refactored my code to permit the core module to be torn down without affecting node-webgl and using a before block to initialize it, like so:
// Run this before all tests to ensure node-webgl is initialized
before(function () {
if (!global._document) {
global._document = WebGL.document();
global._document.setTitle('Machination Graphics Test Suite');
}
if (!global._canvas) {
global._canvas = global._document.createElement('canvas', 640, 480);
}
});
describe('test suite goes here', function () {
// Ensure core is ready for us before testing (except when testing core)
before(function () {
if (!core.isInitialized()) {
core.init(global._canvas);
}
});
// Tear down core after all tests are run
after(function () {
core.deinit();
});
...
});
Use before() as described in their documentation.
describe('hooks', function() {
before(function() {
// runs before all tests in this block
});
......
});
the function in before will run first and everything else int he describe after it.
hope this helps.
So I have a folder full of scripts that all resemble a structure like this
// Adapter-100.js
angular.module('myModule', ['myParentFactory', function(myParentFactory) {
return angular.extend(myParentFactory, {
"someFunctionA" : function() {},
"someFunctionB" : function() {},
"someFunctionC" : function() {}
});
}]);
And my test just checks that they have these three methods, trouble is there is about 100 of these files (they're adapters for communicating with a server)
Here is a representation of my tests file
// api-adapter-tests.js
describe('Unit: EndPointMethods', function() {
var values, factory, adapter;
// Boot the module
beforeEach(function() {
module('myModule');
inject(function ($injector) {
values = $injector.get('AppConsts');
factory = $injector.get('EndPointConnection');
adapter = $injector.get('TestAdapter'); // This needs to change to match what adapter is being tested
});
});
// Run some tests
describe('AppConsts', function() {
it('Should have an api_host key', function() {
expect(values).toBeDefined();
expect(values.api_host).toBeDefined();
expect(typeof values.api_host).toBe('string');
});
});
// Is this able to be generated to test each adapter independently?
describe('TestEndPointMethod has minimum functional definitions', function() {
it('should have 3 defined functions', function() {
expect(factory.consumeResponse).toBeDefined();
expect(factory.getEndPoint).toBeDefined();
expect(factory.mutator).toBeDefined();
});
});
});
I don't want to have to write a separate describes/it block for each adapter but rather have Karma loop over all of these and create the tests on the fly (the tests are very unlikely to ever change)
I've Googled around for a solution to this but can't seem to find anything that does this kind of thing for me.
You can wrap your suites in a clojure and pass the Adapter you want to test: mocha will take care of running it in the right way - and so Karma.
function runSuiteFor(newAdapter){
return function(){
// api-adapter-tests.js
describe('Unit: EndPointMethods', function() {
var values, factory, adapter;
// Boot the module
beforeEach(function() {
module('myModule');
inject(function ($injector) {
values = $injector.get('AppConsts');
factory = $injector.get('EndPointConnection');
adapter = $injector.get(newAdapter); // set the Adapter here
});
});
// Run some tests
describe('AppConsts', function() {
it('Should have an api_host key', function() {
expect(values).toBeDefined();
expect(values.api_host).toBeDefined();
expect(typeof values.api_host).toBe('string');
});
});
// Is this able to be generated to test each adapter independently?
describe('TestEndPointMethod has minimum functional definitions', function() {
it('should have 3 defined functions', function() {
expect(factory.consumeResponse).toBeDefined();
expect(factory.getEndPoint).toBeDefined();
expect(factory.mutator).toBeDefined();
});
});
});
}
}
var adapters = ['MyTestAdapter1', 'MyTestAdapter2', etc...];
for( var i=0; i<adapters.length; i++){
runSuiteFor(adapters[i])();
}
Note: IE8 has some issues with this approach sometimes, so in case you're with Angular 1.2 bare in mind this.
I have an angular service called requestNotificationChannel:
app.factory("requestNotificationChannel", function($rootScope) {
var _DELETE_MESSAGE_ = "_DELETE_MESSAGE_";
function deleteMessage(id, index) {
$rootScope.$broadcast(_DELETE_MESSAGE_, { id: id, index: index });
};
return {
deleteMessage: deleteMessage
};
});
I am trying to unit test this service using jasmine:
"use strict";
describe("Request Notification Channel", function() {
var requestNotificationChannel, rootScope, scope;
beforeEach(function(_requestNotificationChannel_) {
module("messageAppModule");
inject(function($injector, _requestNotificationChannel_) {
rootScope = $injector.get("$rootScope");
scope = rootScope.$new();
requestNotificationChannel = _requestNotificationChannel_;
})
spyOn(rootScope, '$broadcast');
});
it("should broadcast delete message notification", function(done) {
requestNotificationChannel.deleteMessage(1, 4);
expect(rootScope.$broadcast).toHaveBeenCalledWith("_DELETE_MESSAGE_", { id: 1, index: 4 });
done();
});
});
I read about the Asynchronous Support in Jasmine, but as I am rather new to unit testing with javascript couldn't make it work.
I am receiving an error :
Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL
and my test is taking too long to execute (about 5s).
Can somebody help me providing working example of my code with some explanation?
Having an argument in your it function (done in the code below) will cause Jasmine to attempt an async call.
//this block signature will trigger async behavior.
it("should work", function(done){
//...
});
//this block signature will run synchronously
it("should work", function(){
//...
});
It doesn't make a difference what the done argument is named, its existence is all that matters. I ran into this issue from too much copy/pasta.
The Jasmine Asynchronous Support docs note that argument (named done above) is a callback that can be called to let Jasmine know when an asynchronous function is complete. If you never call it, Jasmine will never know your test is done and will eventually timeout.
Even for async tests, there is a timeout that goes off in this cases, You can work around this error by increasing the value for the limit timeout to evaluate an async Jasmine callback
describe('Helper', function () {
var originalTimeout;
beforeEach(function() {
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000000;
});
afterEach(function() {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
});
it('Template advance', function(doneFn) {
$.ajax({
url: 'public/your-end-point.mock.json',
dataType: 'json',
success: function (data, response) {
// Here your expected using data
expect(1).toBe(1)
doneFn();
},
error: function (data, response) {
// Here your expected using data
expect(1).toBe(1)
doneFn();
}
});
});
});
Source: http://jasmine.github.io/2.0/introduction.html#section-42
This error can also be caused by leaving out inject when initializing a service/factory or whatever. For example, it can be thrown by doing this:
var service;
beforeEach(function(_TestService_) {
service = _TestService_;
});
To fix it just wrap the function with inject to properly retrieve the service:
var service;
beforeEach(inject(function(_TestService_) {
service = _TestService_;
}));
import { fakeAsync, ComponentFixture, TestBed } from '#angular/core/testing';
use fakeAsync
beforeEach(fakeAsync (() => {
//your code
}));
describe('Intilalize', () => {
it('should have a defined component', fakeAsync(() => {
createComponent();
expect(_AddComponent.ngOnInit).toBeDefined();
}));
});
You can use karma-jasmine plugin to set the default time out interval globally.
Add this config in karma.conf.js
module.exports = function(config) {
config.set({
client: {
jasmine: {
timeoutInterval: 10000
}
}
})
}
This error started out of the blue for me, on a test that had always worked. I couldn't find any suggestions that helped until I noticed my Macbook was running sluggishly. I noticed the CPU was pegged by another process, which I killed. The Jasmine async error disappeared and my tests are fine once again.
Don't ask me why, I don't know. But in my circumstance it seemed to be a lack of system resources at fault.
This is more of an observation than an answer, but it may help others who were as frustrated as I was.
I kept getting this error from two tests in my suite. I thought I had simply broken the tests with the refactoring I was doing, so after backing out changes didn't work, I reverted to earlier code, twice (two revisions back) thinking it'd get rid of the error. Doing so changed nothing. I chased my tail all day yesterday, and part of this morning without resolving the issue.
I got frustrated and checked out the code onto a laptop this morning. Ran the entire test suite (about 180 tests), no errors. So the errors were never in the code or tests. Went back to my dev box and rebooted it to clear anything in memory that might have been causing the issue. No change, same errors on the same two tests. So I deleted the directory from my machine, and checked it back out. Voila! No errors.
No idea what caused it, or how to fix it, but deleting the working directory and checking it back out fixed whatever it was.
Hope this helps someone.
You also get this error when expecting something in the beforeAll function!
describe('...', function () {
beforeAll(function () {
...
expect(element(by.css('[id="title"]')).isDisplayed()).toBe(true);
});
it('should successfully ...', function () {
}
}
Don't use done, just leave the function call empty.
It looks like the test is waiting for some callback that never comes. It's likely because the test is not executed with asynchronous behavior.
First, see if just using fakeAsync in your "it" scenario:
it('should do something', fakeAsync(() => {
You can also use flush() to wait for the microTask queue to finish or tick() to wait a specified amount of time.
In my case, this error was caused by improper use of "fixture.detectChanges()" It seems this method is an event listener (async) which will only respond a callback when changes are detected. If no changes are detected it will not invoke the callback, resulting in a timeout error. Hope this helps :)
Works after removing the scope reference and the function arguments:
"use strict";
describe("Request Notification Channel", function() {
var requestNotificationChannel, rootScope;
beforeEach(function() {
module("messageAppModule");
inject(function($injector, _requestNotificationChannel_) {
rootScope = $injector.get("$rootScope");
requestNotificationChannel = _requestNotificationChannel_;
})
spyOn(rootScope, "$broadcast");
});
it("should broadcast delete message notification with provided params", function() {
requestNotificationChannel.deleteMessage(1, 4);
expect(rootScope.$broadcast).toHaveBeenCalledWith("_DELETE_MESSAGE_", { id: 1, index: 4} );
});
});
What I did was: Added/Updated the following code:
framework: 'jasmine',
jasmineNodeOpts:
{
// Jasmine default timeout
defaultTimeoutInterval: 60000,
expectationResultHandler(passed, assertion)
{
// do something
},
}
As noted by #mastablasta, but also to add that if you call the 'done' argument or rather name it completed you just call the callback completed() in your test when it's done.
// this block signature will trigger async behavior.
it("should work", function(done){
// do stuff and then call done...
done();
});
// this block signature will run synchronously
it("should work", function(){
//...
});
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
Keeping this in the block solved my issue.
it('', () => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
});
Instead of
beforeEach(() => {..
use
beforeEach(fakeAsync(() => {..
In my case, a timeout was cause because of a failed injection of a service with providedIn: 'root'. It's not clear why injection failed, nor why there was no early error if there is apparently no instance of provider available.
I was able to work around it by manually providing a value:
TestBed.configureTestingModule({
declarations: [
// ...
],
imports: [
// ...
],
providers: [
// ...
{ provide: MyService, useValue: { /* ... */ } },
]
}).compileComponents();
I have caught the same error because I used the setTimeout function in the component. Example:
ngOnInit(): void {
this.changeState();
}
private changeState(): void {
setTimeout(() => this.state = StateEnum.IN_PROGRESS, 10000);
}
When I changed the timeout from 10000ms to 0 or less than 5000ms (DEFAULT_TIMEOUT_INTERVAL), all tests were passed.
In my case, I was not returning the value from the spy method, hence facing error,
mainMethod(args): Observable<something>{
return nestedMethod().pipe();
}
Your Test should like below,
it('your test case', (done: DoneFn) => {
const testData = {}; // Your data
spyOn(service, 'nestedMethod').and.returnValue(of(testData));
const obxValue = service.mainMethod('your args');
obxValue.pipe(first()).subscribe((data) => {
expect(data).not.toBeUndefined();
done();
});
});
If you have an argument (done) in the it function try to remove it as well it's call within the function itself:
it("should broadcast delete message notification", function(/*done -> YOU SHOULD REMOVE IT */) {
requestNotificationChannel.deleteMessage(1, 4);
expect(rootScope.$broadcast).toHaveBeenCalledWith("_DELETE_MESSAGE_", { id: 1, index: 4 });
// done(); -> YOU SHOULD REMOVE IT
});
I am trying to test a function that requires a module using jasmine and requirejs.
Here is a dummy code:
define("testModule", function() {
return 123;
});
var test = function() {
require(['testModule'], function(testModule) {
return testModule + 1;
});
}
describe("Async requirejs test", function() {
it("should works", function() {
expect(test()).toBe(124);
});
});
It fails, because it is an async method. How can I perform a test with it?
Note: I dont want to change my code, just my tests describe function.
For testing of an asynchronous stuff check runs(), waits() and waitsFor():
https://github.com/pivotal/jasmine/wiki/Asynchronous-specs
Though this way looks a bit ugly as for me, therefore you could also consider following options.
1. I'd recommend you to try jasmine.async that allows you to write asynchronous test cases in this way:
// Run an async expectation
async.it("did stuff", function(done) {
// Simulate async code:
setTimeout(function() {
expect(1).toBe(1);
// All async stuff done, and spec asserted
done();
});
});
2. Also you can run your tests inside require's callback:
require([
'testModule',
'anotherTestModule'
], function(testModule, anotherTestModule) {
describe('My Great Modules', function() {
describe('testModule', function() {
it('should be defined', function() {
expect(testModule).toBeDefined();
});
});
describe('anotherTestModule', function() {
it('should be defined', function() {
expect(anoterTestModule).toBeDefined();
});
});
});
});
3. Another point is I guess that this code is not working as you're expecting:
var test = function() {
require(['testModule'], function(testModule) {
return testModule + 1;
});
};
Because if you call test(), it won't return you testModule + 1.