2

So I have such decorator in app config:

    angular.module('app').config(['$provide', function ($provide) {

    $provide.decorator('$rootScope', ['$delegate', function ($delegate) {
        $delegate.constructor.prototype.$onRootScope = function (name, listener) {
            var unsubscribe = $delegate.$on(name, listener);
            this.$on('$destroy', unsubscribe);
        };

        $delegate.constructor.prototype.$watchRootScope = function (name, listener) {
            var unsubscribe = $delegate.$watch(name, listener);
            this.$on('$destroy', unsubscribe);
        };

        $delegate.constructor.prototype.$watchAfterLoad = function (watchExpression, listener, objectEquality) {
            var initialLoad = true;
            this.$watch(watchExpression, function () {
                if (initialLoad) {
                    // note: this obviously runs outside of angular, so sometimes the timeout could run after initial load
                    setTimeout(function () { initialLoad = false; }, 25);
                } else {
                    listener.apply(this, arguments);
                }
            }, objectEquality);
        };

        return $delegate;
    }]);

}]);

As you can see this decorator lets me use $scope.$onRootScope instead of $rootScope.$on and takes care of automatic listeners removal on scope destroy event...

When I unit test my code which logic contains $scope.$onRootScope I'm getting such error: TypeError: undefined is not a constructor (evaluating 'scope.$onRootScope') in

Before each test I'm loading all required models and do inject which looks like this ~:

beforeEach(function () {
    inject(function (_$rootScope_) {
        $rootScope = _$rootScope_;
    });
});

How should I overcome this problem? Is there a way to mock / mimic $scope.$onRootScope behaviour?

I'm quite new to unit testing & Jasmine so sorry for not very nicely formatted question.

EDIT #1:

As I'm mocking my $scope object (var $scope = {...}) before passing it as argument to service method which I'm testing I can avoid error by simply defining $scope method:

$scope = {
   ...
   $onRootScope: function() {}
}

Still awaiting for some better ideas :-)

1 Answer 1

2

I believe you need to build your $scope based off of the decorated $rootScope, as opposed to creating a new dummy object.

Like so:

var $root, $scope;

beforeEach(function () {
  module('app');

  inject(function ($rootScope) {
    $root  = $rootScope;
    $scope = $root.$new();
  });
});

it('should have the expected property', function () {
  expect($scope.constructor.prototype).to.have.property('$watchRootScope');
});

I'll chuck in a link to the spec suite of a mini-lib I put together some time ago, doing roughly the same thing you are now.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks man, you put me on the right path :) My objective was not to test if the property exists, but to make it work. $scope = $root.$new(); - I didin't know you can create scope like this, so I was mocking $scope as simple object... Which obviously was missing constructor $onRootScope then... Thanks for the answer!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.