The following async promise lives inside the Angular target that I am testing. I am unable to see the code within the 'then' being invoked and it it this code within the 'then' that I require to test.
angular.module('Ls', [
])
function Locale($rootScope, $http) {
var API = {
getAvailables: getAvailables
};
API.getAvailables().then(function(data) {
..........do stuff
........it what this code is doing that I want to test!!!
});
function getAvailables() {
return $http.get('/l.json').then(function(response) {
return response.data;
});
}
You can either stub getAvailables to always resolve, or you could extract the callback to a separate function and test that.
Option A
sinon.stub(API, 'getAvailables').resolves(data);
Option B
API.getAvailables().then(handleResponse);
^ Test handleResponse function
This fixed it for me
$httpBackend.flush()
https://docs.angularjs.org/api/ng/service/$http#writing-unit-tests-that-use-http
Related
Does $qinclude an implicit digest/apply upon resolution/rejection of the promise chain?
I have migrated a piece of code from $q to use q and a digest now appears to be missing resulting in different behavior. Why might this be?
Even more - it includes explicit as long as QProvider is defined like:
function $QProvider() {
this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
return qFactory(function(callback) {
$rootScope.$evalAsync(callback); // ! $evalAsync()
}, $exceptionHandler);
}];
}
From this function declaration there is short way to method .$evalAsync() that for versin 1.2.28 looks like
$evalAsync: function(expr) {
// if we are outside of an $digest loop and this is the first time we are scheduling async
// task also schedule async auto-flush
if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) {
$browser.defer(function() {
if ($rootScope.$$asyncQueue.length) {
$rootScope.$digest(); // ! $digest()
}
});
}
this.$$asyncQueue.push({scope: this, expression: expr});
},
Hello I have problem with .then() function while trying to implement jasmine unit testing.
Here is my code:
describe("getBuilding", function () {
it("checks getBuilding", function () {
var id_building = 4;
LocalDB.getTestData();
LocalDB.getBuilding(id_building).then(function (result) {
expect(result.name).toMatch("Something");
});
});
});
In this case, the result variable has a right value in then() function, but expect just doesnt work here. If i change "Something" to "something else" the tests will still succes, althought it should't.
I tried to solve it like this:
describe("getBuilding", function () {
it("checks getBuilding", function () {
var id_building = 4;
LocalDB.getTestData();
expect(LocalDB.getBuilding(id_building).name).toMatch("Something");
});
});
or
describe("getBuilding", function () {
it("checks getBuilding", function () {
var finalResult;
var id_building = 4;
LocalDB.getTestData();
LocalDB.getBuilding(id_building).then(function (result) {
finalResult=result.name;
});
expect(finalResult).toMatch("Something");
});
});
But in both cases, the value that's being matched is undefined. Can anyone give some advice pls?
Your 'then()' is probably not being run at all - since the promise is resolved asynchronously, you need to either ensure that the promises are resolved before exiting the test or use jasmine async to ensure that jasmine waits for the async method to resolve before moving on to the next test.
In unit tests with promises, often you need to manually notify the angularjs lifecycle that it's time for promises to be resolved.
Try bringing in the $rootScope dependency and adding a call to $rootScope.$digest() at the end of your test.
describe("getBuilding", function () {
it("checks getBuilding", function () {
var id_building = 4;
LocalDB.getTestData();
LocalDB.getBuilding(id_building).then(function (result) {
expect(result.name).toMatch("Something");
});
$rootScope.$digest();
});
});
If that doesn't work by itself, you may also need to use Jasmine Async
describe("getBuilding", function () {
it("checks getBuilding", function (done) {
var id_building = 4;
LocalDB.getTestData();
LocalDB.getBuilding(id_building).then(function (result) {
expect(result.name).toMatch("Something");
done();
}, function(){
done.fail('The promise was rejected');
});
$rootScope.$digest();
});
});
The jasmine async way using done is a better test, because it will fail if the promise is rejected. The first would silently pass if the promise was rejected. However, if you know that this particular promise will always resolve, the first way may be good enough for your scenario.
According to :http://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support
Jasmine has support for async methods. Essentially your issue is your asserting before your async call has responded. Therefore it has no data and your assert fails.
So according to the above link you could do something like (not tested, i haven't used jasmine so cannot be certain im following the link correctly either. Hopefully you can understand better than i).
describe("getBuilding", function () {
it("checks getBuilding", function () {
var id_building = 4;
LocalDB.getBuilding(id_building).then(function (result) {
expect(result.name).toMatch("Something");
done();
});
});
});
Try to return the promise.
return LocalDB.getBuilding(id_building)
.then(function (result) {
expect(result.name).toMatch("Something");
});
I am using a database library that its callback-based interface looks like this:
var DB = {
insert: function(options, callback) {
}
}
I want to implement a wrapper around this database to convert its callback style API to a promise based API. To do this I have defined the following class:
var DatabaseWrapper = {
init: function(db) {
this.db = db;
},
insert: function(options) {
return Q.denodeify(this.db.insert.bind(this.db))(options);
}
}
I want to write a unit test to ensure that when I call DatabaseWrapper.insert it calls DB.insert. So far my test looks like this:
describe('DatabaseWrapper', function () {
var wrapper, insertSpy, bindStub;
beforeEach(function () {
wrapper = Object.create(DatabaseWrapper);
insertSpy = sinon.spy(function () {
console.log('insertSpy got called');
});
bindStub = sinon.stub();
wrapper.db = {
insert: function (options, callback) {
}
};
sinon.stub(wrapper.db.insert, 'bind').returns(insertSpy);
});
describe('#insert', function () {
it('should delegate to db.insert', function (done) {
wrapper.insert({herp: 'derp'});
expect(wrapper.db.insert.bind).to.have.been.calledOnce;
// This fails but I expect it to succeed
expect(promise).to.have.been.calledOnce;
})
});
});
The DB instance's insert method is actually getting called as after the test fails, as the 'insertSpy got called' message is printed in the console.
But apparently it gets called after the test has failed.
As far as I know, this is due to the way Node's process.nextTick works. So the call to the callback happens after the test fails. Is there a way I can fix this test without relying on third-party libraries (e.g. q-flush)?
You're performing an asynchronous action so it's best to perform an asynchronous test. Adding a setTimeout still leaves you prone to race conditions.
describe('#insert', function () {
it('should delegate to db.insert', function () { // no done here
// note the return here to signal to mocha this is a promise test
return wrapper.insert({herp: 'derp'}).then(function(){
// add expects here, rest of asserts should happen here
expect(wrapper.db.insert.bind).to.have.been.calledOnce;
});
})
});
});
I have a block of code that I will be using several times within a then statement in mocha so I turned it into a function. However I also need to call done()within that function and it's out of scope resulting in the error Uncaught ReferenceError: done is not defined. Here's a code snippet:
var collectionChecker = function(results) {
expect(Array.isArray(results), 'Did not return collection');
expect(results.length === numAttr, 'Returned wrong number of models');
done();
};
test('returns a collection with correct number of models', function(done) {
attrs.getAttributeTemplates().then(collectionChecker);
});
How can I pass done() to my function?
I found a workaround by chaining another .then and calling done there but it seems like an ugly way to do it.
You're overthinking it - mocha supports promises, you can return a promise and if it is fulfilled the test will pass (and if the expects throw it will fail):
var collectionChecker = function(results) {
expect(Array.isArray(results), 'Did not return collection');
expect(results.length === numAttr, 'Returned wrong number of models');
};
// just add a return, no `done` here or anywhere
test('returns a collection with correct number of models', function() {
return attrs.getAttributeTemplates().then(collectionChecker);
});
I have a problem with testing my function that I use with typeahead.js (https://github.com/angular-ui/bootstrap/blob/master/src/typeahead/typeahead.js). I usually know how to resolve a promise in tests, but not with the following function:
$scope.getSuggestion = function ( name, length ) {
return $http.get( 'api/autocomplete/?contains=' + name )
.then( function ( response ) {
return response.data.slice( 0, length || 7 );
});
};
My test looks like this:
describe('Suggestions', function () {
it('should be possible to get suggestions', function () {
$httpBackend.expectGET('api/autocomplete?title__contains=Foo').respond([
{ name: 'Foobar' },
{ name: 'Foobala' },
{ name: 'Foolooloo' }
]);
var suggestions = $scope.getSuggestion( 'Foo' );
$rootScope.$apply();
// Here should be a test.
console.log(suggestions);
})
});
But suggestion only is the promise object Object{then: function (callback, errback) { ... }}.
Where did I mess up!?
suggestions is a promise, it is not an actual value, you need to call then() to get the value out of it. That is
suggestions.then(function(data) {
// Here should be a test.
console.log(data);
});
Update:
Try this:
describe('Suggestions', function () {
it('should be possible to get suggestions', function () {
$httpBackend.expectGET('api/autocomplete?title__contains=Foo').respond([
{ name: 'Foobar' },
{ name: 'Foobala' },
{ name: 'Foolooloo' }
]);
var suggestions;
$scope.getSuggestion( 'Foo' ).then(function(data) {
suggestions = data;
});
$httpBackend.flush();
$rootScope.$apply(); // might be optional
// Here should be a test.
console.log(suggestions);
})
});
Regarding the $httpBackend service, in order to 'force' it to respond to the request of the $http service, it is sufficient to cal $httpBackEnd.flush();. I believe the $rootScope.$apply(); is redundant here.
Regarding the return value of the $scope.getSuggestion method, note that it will not return you the data from the server; it will return you a promise object which will be resolved once the request to the server is fulfilled.
Also note that promises can be chained, therefore the result of $http(...).then(....) in your method is still a promise.
Finally, the return statement in the callback passed as argument in then in your controller (return response.data.slice( 0, length || 7 );) will not be of much use; when the promise is resolved and this callback is invoked, you won't be able to get that return value.
You can of course provide code in the callback passed to then inside the controller method if there is something you need to do every time you call the getSuggestion method. If the 'client' of that method however needs the data the $http service returns, he will have to register his own callback to retrieve them.
So, to actually get the response data in your test (the 'client' of your controller method), you need to register the respective callbacks inside the test.
You can use the standard then method of the promise "interface" which expects you to pass two callbacks; the first will be called if the request succeeds (in your case if the $httpBackend is 'trained' to respond with a status in the 2XX range), the second one in case of an error. Alternatively you could use the AngularJS specific success and error methods since the $http service returns an HttpPromise.
You can check out this approach here: http://jsfiddle.net/yianisn/rgk97/
See also here and here.