3

I am trying to write unit tests for a directive that uses a link function and does not have any template associated with it. The directive requires ngModel like this:

angular.module('some-module', []).directive('someDirective', function() {
  return {
    restrict: 'A',
    require: 'ngModel',
    link: function(scope, element, attr, controller) {
      //Do something
    }
  };
});

When trying to unit test this, I'm just trying to compile a DIV to trigger the directive:

var $scope = $rootScope.$new();
var element = $compile('<div some-directive></div>')($scope);
$scope.$digest();

But this triggers an error that the ngModel controller cannot be found. After looking at the documentation for ngModel and ngModelController I've tried mocking ngModel using the $provide service like this:

beforeEach(module(function($provide) {
  var mockNgModel = {};
  $provide.value('ngModel', mockNgModel); //Doesn't work
  $provide.value('ngModelCtrl', mockNgModel); //Doesn't work
  $provide.value('ngModel.NgModelController', mockNgModel); //Doesn't work
  $provide.value('ngModel.ngModelController', mockNgModel); //Doesn't work
}));

I've also seen other solutions that talk about creating a property on the scope which contains whatever ngModel is bound to in the HTML where your directive is being used (e.g. solution1, solution2) but this has not solved the error in my case.

Community
  • 1
  • 1
A. Duff
  • 4,097
  • 7
  • 38
  • 69

1 Answers1

3
var $scope = $rootScope.$new();
$scope.foo = 'bar';
var element = $compile('<div some-directive ng-model="foo"></div>')($scope);
$scope.$digest();
HankScorpio
  • 3,612
  • 15
  • 27
  • I still get the same error. Is this working for you? If so, then I may need to identify another problem in my code. – A. Duff Mar 03 '15 at 19:31
  • Yeah it does, check it out: https://jsfiddle.net/A8Vgk/1232/ If i leave out ng-model, i get the error you described. – HankScorpio Mar 03 '15 at 19:43
  • Looks like you're right. For some reason, it's trying to mock the `ngModel` that causes the error. When I remove those lines and specify `ng-model` in the HTML tag, I no longer get the error. I wonder if you should not try to mock `ngModel` in unit tests, then. – A. Duff Mar 03 '15 at 20:54
  • 3
    I wouldn't mock any framework stuff, for the same reason we don't mock core JS functions and libraries like the Math library. My reasoning is that all of the angular framework is unit-tested, so we expect that they work as advertised. There also isn't any ease or benefit to the developer by mocking ngModel. – HankScorpio Mar 03 '15 at 21:51
  • But what if we want to spy on it, like to make sure that the right property on ngModel is called? – A. Duff Mar 03 '15 at 22:12
  • 1
    Write a test that proves your directive correctly gets expanded into the elements you expect. To prove that the template is using the model, you can use jQuery selectors to find that element and inspect its contents. expect(testElement.html()).toContain('my model value'); To prove that it's working the other way around, do something that would cause your directive to change the model, then just test the scope property: expect(scope.myProperty).toEqual('bananas'); In both cases we don't actually need to do anything directly with ngModel except ensure that it's being using in our setup. – HankScorpio Mar 03 '15 at 22:23