We're unit testing our controllers. We've successfully mocked the call to our REST service layer and verified that it is indeed being called with the given data. Now however we'd like to test that in our controller the execution of the then promise changes the location.path:
controller:
(function () {
    app.controller('registerController', ['$scope', '$location', '$ourRestWrapper', function ($scope, $location, $ourRestWrapper) {
    $scope.submitReg = function(){
        // test will execute this
        var promise = $ourRestWrapper.post('user/registration', $scope.register);
        promise.then(function(response) {    
                console.log("success!"); // test never hits here           
                $location.path("/");
        },
            function(error) {
                console.log("error!"); // test never hits here
                $location.path("/error");
            }
        );
    };
$ourRestWrapper.post(url,data) just wraps Restangular.all(url).post(data)..
Our Test:
(function () {
    describe("controller: registerController", function() {
        var scope, location, restMock, controller, q, deferred;
        beforeEach(module("ourModule"));
        beforeEach(function() {
            restMock = {
                post: function(url, model) {
                    console.log("deferring...");
                    deferred = q.defer();    
                    return deferred.promise;
                }
            };
        });
        // init controller for test
        beforeEach(inject(function($controller, $rootScope, $ourRestWrapper, $location, $q){
            scope = $rootScope.$new();
            location = $location;
            q = $q;
            controller = $controller('registerController', {
                $scope: scope, $location: location, $ourRestWrapper: restMock});
        }));
    it('should call REST layer with registration request', function() {
        scope.register = {data:'test'};
        spyOn(restMock, 'post').andCallThrough();
        scope.submitReg();
        deferred.resolve();
        // successfull
        expect(restMock.post).toHaveBeenCalledWith('user/registration',scope.register);
        expect(restMock.post.calls.length).toEqual(1);
        // fail: Expected '' to be '/'.
        expect(location.path()).toBe('/');
    });
In our console we see "deferring..." and the first two expectations succeed. Why will it not call the then block (i.e. set the location)?
$q is integrated with the $rootScope. Scope Scope model observation mechanism in AngularJS, which means faster propagation of resolution or rejection into your models and avoiding unnecessary browser repaints, which would result in flickering UI.
$q. defer() allows you to create a promise object which you might want to return to the function that called your login function. Make sure you return deferred.
If the function passed to Jasmine takes an argument (traditionally called done ), Jasmine will pass a function to be invoked when asynchronous work has been completed.
Promises in AngularJS are provided by the built-in $q service. They provide a way to execute asynchronous functions in series by registering them with a promise object. {info} Promises have made their way into native JavaScript as part of the ES6 specification.
Cache the $rootscope object when you get it from the injector and call $rootScope.$apply() immediately after deferred.resolve().
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With