What is the proper way to test with Jasmine the following?:
$.when(this.collection1.fetch()).done(()=> {
$.when(this.collection2.fetch({reset: true})).done(()=> {
this.render();
});
});
I need to make sure that first the this.collection1.fetch() is finished before the second request started.
Related
I'm new to jasmine framework. I've gone through some tutorials and learned and started writing unit tests. 'facing one issue here is the description.
I have a controller where i can invoke a service call to get the data. See the code below.
$scope.getEmpInfo = function() {
EmpService.getInfo($scope.empid)
.then(function(data) {
$scope.empData = data;
$scope.populateEmpData($scope.empData);
}, function(reason) {
//do nothing
}
}
Now, i want to write a unit test for the above method. Im able to make a spy on serice using promise but i wasnt able to spy $scope.populateEmpData(). here is my test case.
describe('Emp data', function() {
var d, scope;
beforeEach(function() {
module("emp");
module("emo.info");
});
describe('empcontroller', function() {
beforeEach(inject(function($q,_EmpService_, $controller,$rootScope){
d = $q.defer();
empService = _EmpService_;
spyOn(empService,"getInfo").and.returnValue(d.promise);
scope = $rootScope.$new();
empCtrl = $controller("empController", {
$scope: scope,
});
}));
it('should get the Employee information ', function() {
scope.getEmpInfo();
spyOn(scope,'populateEmpData');
expect(EmpService.getInfo).toHaveBeenCalled();
//Here im getting the error.
expect(scope.populateEmpData).toHaveBeenCalled();
});
});
});
Please help resolve this issue. Thanks in advance.
It's because you are not resolving promise. You will have to make change in spyOn.
- spyOn(empService,"getInfo").and.callFake(function() {
return {
then : function(success, error) {
success();
}
} }
Now, it will go into the success callback and will try to call $scope.populateEmpData();
You're never resolving your promise. And you need to call $scope.$apply().
Why is this necessary? Because any promises made with the $q service
to be resolved/rejected are processed upon each run of angular’s
digest cycle. Conceptually, the call to .resolve changes the state of
the promise and adds it to a queue. Each time angular’s digest cycle
runs, any outstanding promises will be processed and removed from the
queue.
Unit Testing with $q Promises in AngularJS
Check it out above link it will help you.
I am using jasmine to write unit tests for an angularJS factory that returns a call to Restangular. Take for example,
function funcToBeTested(params) {
return Restangular.all('/foo').getList(params);
}
It is straightforward to test whether or not all() is being called on Restangular. How can I test whether getList() gets called on the returned Restangular object after all() has returned?
At the moment, I have
beforeEach(function() {
module('ui.router');
module('myService');
module('restangular');
});
describe('funcToBeTested', function() {
beforeEach(function() {
httpBackend.expectGET('/foo').respond([]);
});
it('should call all on Restangular', function() {
spyOn(Restangular, 'all').and.callThrough();
funcToBeTested({});
httpBackend.flush();
expect(Restangular.all).toHaveBeenCalled();
});
});
If I try to follow that same pattern and do something along the lines of
expect(Restangular.all.getList).toHaveBeenCalled();
things stop working. I was not able to find any literature on this. Any help is appreciated.
In code below, userService.addPreference is mocked, and so is $state.go, but still the call count of $state.go is always zero. Is there something I may have missed in the setup of userService.addPreference mocked method?
Code that is being unit tested
userService.addPreference(preference).then(function (dashboard) {
$state.go('authenticated.dashboard.grid', {id: dashboard.id});
});
Unit Test Mocked methods and the Unit Test
sinon.stub(userService, 'addPreference', function (preference) {
var defer = $q.defer();
defer.resolve(preference);
return defer.promise;
});
sinon.stub($state, 'go', function () { });
it('dashboard.confirm should call $state.go', function () {
vm.confirm();//this is the function containing code being unit tested
expect($state.go.callCount).to.equal(1);//this is always ZERO and so failing
});
The service call
userService.addPreference(preference).then(function (dashboard) {
$state.go('authenticated.dashboard.grid', {id: dashboard.id});
});
involves a async callback, which will not fire unless we explicitly tell it to. To force the callback to evaluate we need to run a digest cycle using $scope.$apply, so change your test code to:
it('dashboard.confirm should call $state.go', function () {
vm.confirm();//this is the function containing code being unit tested
$scope.$apply();
expect($state.go.callCount).to.equal(1);//this is always ZERO and so failing
});
Remember is a sequential flow callback are never fired.
I am attempting to test that my controller opens a modal when a broadcast event is fired.
The code works fine but the expectation always fails:
it('should open model on idleStart', function () {
$scope.$on('idleStart', function(){
expect(dsModalSpy).toHaveBeenCalled();
});
$scope.$broadcast('idleStart');
});
I assume it fails because of its asynchronous nature of a broadcast. The controller hasn't had time to open the modal.
So I added in the done() method and put it in a setTimeout function
it('should open model on idleStart', function (done) {
$scope.$on('idleStart', function(){
setTimeout(function(){
expect(dsModalSpy).toHaveBeenCalled();
done();
}, 500);
});
$scope.$broadcast('idleStart');
});
It said it was passing now, but I had a suspicion so I put in a debugger statement. The code in the setTimeout is not run until after the test says it has passed successfully.
it('should open modal on idleStart', function (done) {
$scope.$on('idleStart', function(){
setTimeout(function(){
debugger //This code is run after the test says it is completed and passed successfully.
done();
}, 500);
});
$scope.$broadcast('idleStart');
});
I thought by adding the done argument into the it function that it wouldn't say it was done until the done function was called.
Thanks!
npm install karma-jasmine was giving me the 1.x version.
I needed to be using Jasmine 2.0
npm install karma-jasmine#2_0 --save-dev
I'm trying to test my AngularJS controller with Jasmine, using Karma. But a $timeout which works well in real-life, crashes my tests.
Controller:
var Ctrl = function($scope, $timeout) {
$scope.doStuff = function() {
$timeout(function() {
$scope.stuffDone = true;
}, 250);
};
};
Jasmine it block (where $scope and controller have been properly initialized):
it('should do stuff', function() {
runs(function() {
$scope.doStuff();
});
waitsFor(function() {
return $scope.stuffDone;
}, 'Stuff should be done', 750);
runs(function() {
expect($scope.stuffDone).toBeTruthy();
});
});
When I run my app in browser, $timeout function will be executed and $scope.stuffDone will be true. But in my tests, $timeout does nothing, the function is never executed and Jasmine reports error after timing out 750 ms. What could possibly be wrong here?
According to the Angular JS documentation for $timeout, you can use $timeout.flush() to synchronously flush the queue of deferred functions.
Try updating your test to this:
it('should do stuff', function() {
expect($scope.stuffDone).toBeFalsy();
$scope.doStuff();
expect($scope.stuffDone).toBeFalsy();
$timeout.flush();
expect($scope.stuffDone).toBeTruthy();
});
Here is a plunker showing both your original test failing and the new test passing.
As noted in one of the comments, Jasmine setTimeout mock is not being used because angular's JS mock $timeout service is used instead. Personally, I'd rather use Jasmine's because its mocking method lets me test the length of the timeout. You can effectively circumvent it with a simple provider in your unit test:
module(function($provide) {
$provide.constant('$timeout', setTimeout);
});
Note: if you go this route, be sure to call $scope.apply() after jasmine.Clock.tick.
As $timeout is just a wrapper for window.setTimeout, you can use jasmines Clock.useMock() which mocks the window.setTimeout
beforeEach(function() {
jasmine.Clock.useMock();
});
it('should do stuff', function() {
$scope.doStuff();
jasmine.Clock.tick(251);
expect($scope.stuffDone).toBeTruthy();
});