BeforeEach with expectGets cannot be executed more then once - unit-testing

I have a problem with my unit test for a directive where I need to setup the directive before being able to test it.
This is the way I initialize it:
var elm, $templateCache
var count = 0;
beforeEach(module('app/js/directives/templates/mytemplate.html'));
beforeEach(inject(function($injector) {
$templateCache = $injector.get('$templateCache');
}));
beforeEach(inject(
['$httpBackend','$compile','$rootScope', function(_$compile) {
var template = $templateCache.get('app/js/directives/templates/mytemplate.html');
if(count == 0 ){
$httpBackend.expectGET('js/directives/templates/mytemplate.html').respond(template);
}
elm = angular.element('<div my-directive</div>');
$compile(elm)($rootScope);
if(count == 0){
$httpBackend.expectGET('/app/js/content/some.json').respond('{ "left" : "<div>Contact information</div>"}');
$httpBackend.flush();
}
$rootScope.$digest();
count++;
}]
));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it("should work like I want it to", function() {
$rootScope.my-directive.somefunction();
expect($rootScope.somevalue).toBe(2);
});
it("should load the previous page if the function scope.layover.qa_pager.prev is called", function() {
$rootScope.my-directive.someotherfunction();
expect($rootScope.somevalue).toBe(1);
});
This works, but the hack with the count should not be needed in my opinion. If I leave it out I get problems with the $httpbackend which doesn't seem to be reinitialised every time so it gives me 'Error: Unsatisfied requests: GET /app/js/content/some.json' and 'Error: No pending request to flush !'

To me it looks like you are trying to load a template for a unit test. I'm assuming you are using karma to run the tests, so you should look at html2js. It loads your templates into the template cache for you.
https://github.com/karma-runner/karma-ng-html2js-preprocessor
// karma.conf.js
module.exports = function(config) {
config.set({
preprocessors: {
'**/*.html': ['ng-html2js']
},
files: [
'*.js',
'*.html'
],
ngHtml2JsPreprocessor: {
// strip this from the file path
stripPrefix: 'public/',
// prepend this to the
prependPrefix: 'served/',
// or define a custom transform function
cacheIdFromPath: function(filepath) {
return cacheId;
}
}
});
};

Related

why am i getting this Error:connect ECONNREFUSED 127.0.0.1:3000 while writing mocha js test

I have taken a sample random nodejs code named f.js and writing unit test for that.
the node js code is below,
//storing the information in temporary memory
var http = require("http");
var url = require("url");
var parsedUrl = url.parse('/itemsAvailable?model=nokia', true)
// href: '/itemsAvailable?model=nokia',
//search: '?model=nokia',
//query: {model: 'nokia'},
//pathname: '/itemsAvailable'
//information of the user
function reset() {
var d = new Date();
var date = d.getDate();
var hour = d.getHours();
var min = d.getMinutes();
//var time = hour + ':'+min;
//console.log(date,itemsAvailable[2].count,itemsAvailable[2].userId);
if (hour == 16 && min == 52) {
itemsAvailable[2].count = 0;
}
}
exports.reset = reset;
var itemsAvailable = [{
model: 'nokia',
available: 10
},
{
model: 'samsung',
available: 20
},
{
userId: 1234,
count: 0, //initially assigning count to 0
model: "",
}
]; //create an object to store the itemsAvailable
exports.itemsAvailable = itemsAvailable;
var server = http.createServer(function(req, res) {
reset(); //calling the reset after every request
res.write("hello\n");
if (itemsAvailable[2].count == 0) {
if (parsedUrl.query.model === 'nokia' && itemsAvailable[0].available != 0) { //parsedUrl.query gives an object and .model gives nokia
res.write("item chosen is nokia\n")
res.write("item can be bought");
itemsAvailable[2].count++; // increasing the num of mobiles bought
console.log(itemsAvailable[2].count);
itemsAvailable[0].available--;
console.log(itemsAvailable[0].available)
}
} else {
res.write("u cannot buy the item today come back tomorrow");
}
res.end()
}).listen(3000);
exports.server = server;
The test code i have written is below
var assert = require("chai").assert;
var http = require("http");
var Code = require("../f");
describe("itemsAvailable", function() {
it("information count", function() {
assert.equal(Code.itemsAvailable[2].count, 0);
})
});
describe("information count", function() {
it("reset", function() {
if (Code.reset.hour == 16 && Code.reset.min == 52) {
assert.equal(Code.reset.itemsAvailable[2].count, 0);
}
});
})
describe('/', function() {
before(function(done) {
Code.server.listen(3000, done);
});
after(function(done) {
Code.server.close();
});
describe("http request", function() {
it('buy the item', function(done) {
http.get("http://localhost:3000", function(res) {
//assert.equal(Code.server.res,'hello');
try {
if (Code.itemsAvailable[2].count == 0) {
if (Code.parsedUrl.query.model == 'nokia' && Code.itemsAvailable[0].available != 0) {
it("item can be bought", function(done) {
assert.equal(Code.server.res, 'item chosen is nokia');
assert.equal(Code.server.res, 'item can be bought');
done();
})
}
};
} catch (error) {
it("item can not be bought", function(done) {
assert.equal(Code.server.res, 'u cannot buy the item today come back tomorrow');
done();
})
}
})
done();
})
});
})
Iam getting the error
3 passing and 1 failing
1) after all hook uncaught error : error econnrefused 127.0.0.1:3000
iam listening to the port 3000 .The nodejs code alone works fine . I just started learning mocha unit testing , can someone explain me why that error occurs and what changes can be done in the above unit test code to possibly get rid of the error.
There are multiple errors in your code:
You cannot nest it calls. Mocha simply does not support such nesting and will behave erratically if you try it.
In the test buy the item you call done outside your callback to http.get. This is wrong. It causes your test to end prematurely.
This is the direct cause of the error you got. The problem is that http.get only guarantees a result at some undetermined point in the future. By finishing your test prematurely, Mocha moves on to your after hook. (The two it tests that are nested in your buy the item test do not matter: Mocha does not even know about these nested tests at this point.) So Mocha considers that your test is done, and executes the after hook, which closes the server, and after that, it tries to run the request for http.get which fails because the server is closed. The error is reported as an error in the after hook because that's where Mocha is in its sequence of execution when http.get fails.
You fail to call done in your after hook.
Your describe('/' block should be structured like this instead of what you currently have:
describe('/', function() {
before(function(done) {
Code.server.listen(3000, done);
});
after(function(done) {
// Make sure to call the done callback after the server is closed.
Code.server.close(done);
});
describe("http request", function() {
it('buy the item', function(done) {
http.get("http://localhost:3000", function(res) {
// Perform your tests here.
// You must have your done call **inside** the callback to
// http.get.
done();
});
});
});
});
You can add as many it calls with http.get requests in them to cover all the cases you want to cover.

run initializer before ember unit test

I want to write a unit test for a route's method.
routes/tickets
addTicketUserAssoc(ticket, ticketUserAssoc) {
let copy = ticketUserAssoc.copy();
copy.set('ticket', ticket);
ticketUserAssoc.reset();
},
It uses copy and reset on an ember-data record. They are methods which are added during initialization.
initializers/model
export default {
name: 'model',
initialize: function() {
if (alreadyRun) {
return;
} else {
alreadyRun = true;
}
DS.Model.reopen(isValidated, {
copy: function(options){
// some code ...
},
reset() {
// some code ...
}
});
}
};
If I try to import the initializer to the unit test, it does not even appears on the qunit's module list.
Solution
I ended up doing this:
moduleFor('route:tickets', 'Unit | Route | tickets', {
// Specify the other units that are required for this test.
needs: [
// ...
],
beforeEach() {
Ember.run(function() {
application = Ember.Application.create();
application.deferReadiness();
});
}
});
test('assign ticket', function(assert){
let route = this.subject();
let store = route.get('store');
ModelInitializer.initialize(application);
// ...
})

How to execute Ember.RSVP.all within an ember run loop correctly

I'm trying to execute a promise inside Ember.RSVP.all
App.Foo = Ember.Object.create({
bar: function() {
var configuration = ajaxPromise("/api/configuration/", "GET");
Ember.RSVP.all([configuration]).then(function(response) {
//do something with the response in here
});
}
});
But because my integration test mocks the xhr w/out a run loop the test fails with the expected error "You have turned on testing mode, which disabled the run-loop' autorun"
So I wrapped the RSVP with a simple ember.run like so
App.Foo = Ember.Object.create({
bar: function() {
var configuration = ajaxPromise("/api/configuration/", "GET");
Ember.run(function() {
Ember.RSVP.all([configuration]).then(function(response) {
//do something with the response in here
});
});
}
});
But I still get the error for some odd reason. Note -if I run later it's fine (this won't work though as I need to exec the async code for this test to work correctly)
App.Foo = Ember.Object.create({
bar: function() {
var configuration = ajaxPromise("/api/configuration/", "GET");
Ember.run.later(function() {
Ember.RSVP.all([configuration]).then(function(response) {
//do something with the response in here
});
});
}
});
Here is my ajaxPromise implementation -fyi
var ajaxPromise = function(url, type, hash) {
return new Ember.RSVP.Promise(function(resolve, reject) {
hash = hash || {};
hash.url = url;
hash.type = type;
hash.dataType = 'json';
hash.success = function(json) {
Ember.run(null, resolve, json);
};
hash.error = function(json) {
Ember.run(null, reject, json);
};
$.ajax(hash);
});
}
How can I wrap the Ember.RVSP inside my ember run w/out it throwing this error?
Update
here is my test setup (including my helper)
document.write('<div id="ember-testing-container"><div id="wrap"></div></div>');
App.setupForTesting();
App.injectTestHelpers();
test("test this async stuff works", function() {
visit("/").then(function() {
equal(1, 1, "omg");
});
});
The only part I've left out is that I'm using jquery-mockjax so no run loop wraps the xhr mock (and in part that's why I like this library, it fails a test when I don't wrap async code with a run loop as the core team suggests)
This may have to do with how your tests are being run, so if you can provide the test, it will be helpful
I also noticed:
It turns out I believe you are also being (or will be soon) trolled by jQuery's jQXHR object being a malformed promise, the fulfills with itself for 0 reason, and enforcing its own nextTurn on you. Which is causing the autorun. This will only happen in the error scenario.
In ember data we sort this out, by stripping the then off the jQXHR object
see:
https://github.com/emberjs/data/blob/4bca3d7e86043c7c5c4a854052a99dc2b4089be7/packages/ember-data/lib/adapters/rest_adapter.js#L539-L541
I suspect the following will clear this up.
var ajaxPromise = function(url, type, hash) {
return new Ember.RSVP.Promise(function(resolve, reject) {
hash = hash || {};
hash.url = url;
hash.type = type;
hash.dataType = 'json';
hash.success = function(json) {
Ember.run(null, resolve, json);
};
hash.error = function(json) {
if (json && json.then) { json.then = null } // this line
Ember.run(null, reject, json);
};
$.ajax(hash);
});
}
This is rather unfortunate, and various separate concepts and ideas are coming together to cause you pain. We hope to (very shortly) land Ember.ajax which normalizes all these crazy away.
Also feel free to checkout how ember-data is going this: https://github.com/emberjs/data/blob/4bca3d7e86043c7c5c4a854052a99dc2b4089be7/packages/ember-data/lib/adapters/rest_adapter.js#L570-L586
I feel your pain on this Toran, I'm sure it's what Stefan's stated, we had to 1 off mockjax to get our tests to work with it.
https://github.com/kingpin2k/jquery-mockjax/commit/ccd8df8ed7f64672f35490752b95e527c09931b5
// jQuery < 1.4 doesn't have onreadystate change for xhr
if ($.isFunction(onReady)) {
if (mockHandler.isTimeout) {
this.status = -1;
}
Em.run(function () {
onReady.call(self, mockHandler.isTimeout ? 'timeout' : undefined);
});
} else if (mockHandler.isTimeout) {
// Fix for 1.3.2 timeout to keep success from firing.
this.status = -1;
}

Trying to mock $http in Angular

I am trying to create some basic test coverage of a service that I have created. Here is my service:
App.factory('encounterService', function ($resource, $rootScope) {
return {
encounters: [],
encountersTotalCount: 0,
encountersIndex: 0,
resource: $resource('/encounters/:encounterId', {encounterId:'#encounterId'}, {
search: {
method: 'GET',
headers: {
'RemoteUser': 'jjjyyy',
'Content-Type': 'application/json'
}
}
}),
getMoreEncounters: function() {
var that = this;
that.resource.search({}, function(data) {
that.encountersTotalCount = data.metadata.totalCount;
_.each(data.encounters, function(encounter) {
that.encounters.push(encounter);
});
that.busy = false;
that.offset += 10;
$rootScope.$broadcast('encountersFetched');
});
}
};
});
Here is my test that I cannot get to work:
describe('encounterService', function() {
var _encounterService, httpBackend;
beforeEach(inject(function(encounterService, $httpBackend) {
_encounterService = encounterService;
httpBackend = $httpBackend;
var url = '/encounters/';
httpBackend.when('GET', url).respond([{}, {}, {}]);
}));
afterEach(function() {
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
it('should return a list of encounters', function() {
_encounterService.getMoreEncounters();
httpBackend.flush();
expect(_encounterService.encounters.size).toBe(3);
});
});
The error I get is
Chrome 31.0.1650 (Mac OS X 10.8.5) Clinical App services encounterService should return a list of encounters FAILED
Error: Unexpected request: GET encounters
No more request expected
at $httpBackend (/Users/mhamm/Developer/clinical/app/bower_components/angular-mocks/angular-mocks.js:1179:9)
at sendReq (/Users/mhamm/Developer/clinical/app/bower_components/angular/angular.js:7611:9)
at $http.serverRequest (/Users/mhamm/Developer/clinical/app/bower_components/angular/angular.js:7345:16)
at wrappedCallback (/Users/mhamm/Developer/clinical/app/bower_components/angular/angular.js:10549:81)
at wrappedCallback (/Users/mhamm/Developer/clinical/app/bower_components/angular/angular.js:10549:81)
at /Users/mhamm/Developer/clinical/app/bower_components/angular/angular.js:10635:26
at Scope.$eval (/Users/mhamm/Developer/clinical/app/bower_components/angular/angular.js:11528:28)
at Scope.$digest (/Users/mhamm/Developer/clinical/app/bower_components/angular/angular.js:11373:31)
at Function.$httpBackend.flush (/Users/mhamm/Developer/clinical/app/bower_components/angular-mocks/angular-mocks.js:1453:16)
at null.<anonymous> (/Users/mhamm/Developer/clinical/test/spec/clinical.spec.js:78:21)
Error: [$rootScope:inprog] $digest already in progress
http://errors.angularjs.org/1.2.0/$rootScope/inprog?p0=%24digest
at /Users/mhamm/Developer/clinical/app/bower_components/angular/angular.js:78:12
at beginPhase (/Users/mhamm/Developer/clinical/app/bower_components/angular/angular.js:11830:15)
at Scope.$digest (/Users/mhamm/Developer/clinical/app/bower_components/angular/angular.js:11364:9)
at Function.$httpBackend.verifyNoOutstandingExpectation (/Users/mhamm/Developer/clinical/app/bower_components/angular-mocks/angular-mocks.js:1486:16)
at null.<anonymous> (/Users/mhamm/Developer/clinical/test/spec/clinical.spec.js:68:21)
I do not fully understand mocking, so I am sure I am doing something basic incorrectly. Please show me what I am doing wrong.
$resource automatically removes the trailing slashes from the url.
From version 1.3.0 there is a fourth argument that allows you to set stripTrailingSlashes: false to keep those.

Backbone collection parse never fires, collection.models should have 2 items but is 0

Here is my MemberView.js ...
define([
'jquery',
'underscore',
'backbone',
'collections/MembersCollection',
'text!templates/memTemplate.html'
], function($, _, Backbone, MembersCollection, memTemplate) {
var MembersView = Backbone.View.extend({
el: $("#page"),
initialize: function() {
var that = this;
this.collection = new MembersCollection([]);
this.collection.fetch({
success : function(collection, response, options) {
that.render();
},
error: function(collection, response, options) {
console.log('members fetch error: '+response.responseText);
alert(response.responseText);
}
});
},
render: function() {
var data = { members : this.collection.models };
var compiledTemplate = _.template( memTemplate, data );
this.$el.html( compiledTemplate );
}
});
return MembersView;
});
Here is my MemberCollection.js ...
define([
'jquery',
'underscore',
'backbone',
'models/Member'
], function($, _, Backbone, Member) {
var MembersCollection = Backbone.Collection.extend({
model: Member,
initialize : function(models, options) { },
url : '/modular-backbone/server/member',
parse: function (response) {
console.log("In Parse=" + response.length);
return response;
}
});
return MembersCollection;
});
There is never a "In Parse=?" in the console so I have to assume collection.parse is not fireing. Also, if I put a break in the view.render method, collection.models is always a zero length array even though I can clearly see 2 Member records in the fetch success response. What am I missing?
Thanks a lot for your advice :-)
Unbelievable...
I went back and cleaned up a bunch of commented out lines in a few js files, ran the app again and now it's working perfectly. This makes no sense at all.