What is a good way to unit test isolated scope in AngularJS
JSFiddle showing unit test
Directive snippet
scope: {name: '=myGreet'}, link: function (scope, element, attrs) { //show the initial state greet(element, scope[attrs.myGreet]); //listen for changes in the model scope.$watch(attrs.myGreet, function (name) { greet(element, name); }); }
I want to ensure the directive is listening for changes - this does not work with an isolated scope:
it('should watch for changes in the model', function () { var elm; //arrange spyOn(scope, '$watch'); //act elm = compile(validHTML)(scope); //assert expect(scope.$watch.callCount).toBe(1); expect(scope.$watch).toHaveBeenCalledWith('name', jasmine.any(Function)); });
UPDATE: I got it to work by checking if the expected watchers were added to the child scope, but it's very brittle and probably using the accessors in an undocumented way (aka subject to change without notice!).
//this is super brittle, is there a better way!? elm = compile(validHTML)(scope); expect(elm.scope().$$watchers[0].exp).toBe('name');
UPDATE 2: As I mentioned this is brittle! The idea still works but in newer versions of AngularJS the accessor has changed from scope()
to isolateScope()
:
//this is STILL super brittle, is there a better way!? elm = compile(validHTML)(scope); expect(elm.isolateScope().$$watchers[0].exp).toBe('name');
See angular element api docs. If you use element.scope() you get the element's scope that you defined in the scope property of your directive. If you use element.isolateScope() you get the entire isolated scope. For example, if your directive looks something like this :
scope : { myScopeThingy : '=' }, controller : function($scope){ $scope.myIsolatedThingy = 'some value'; }
Then calling element.scope() in your test will return
{ myScopeThingy : 'whatever value this is bound to' }
But if you call element.isolateScope() you'll get
{ myScopeThingy : 'whatever value this is bound to', myIsolatedThingy : 'some value' }
This is true as of angular 1.2.2 or 1.2.3, not sure exactly. In previous versions you had only element.scope().
You can do var isolateScope = myDirectiveElement.scope()
to get the isolate scope.
You don't really need to test that $watch was called though.. that's more testing angularjs than testing your app. But I guess it's just an example for the question.
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