1

I am working on an AngularJS app. I want to be able to implement unit testing through this app. Currently, I'm struggling with unit testing one of my directives. At this time, I have a module setup as follows:

angular.module('my.module', [])
  .controller('myCtrl', function ($scope, $element) {
    // Directive specific business logic goes here
  })
  .directive('myDirective', function() {
    return {
      restrict: 'E',
      transclude: true,
      replace: true,
      scope: {
        title:'=',
    state:'@'
      },
      templateUrl: 'myHtml.tpl.html',
      controller: myCtrl
    };
  })
;

I have split my controller out from my directive because I need to be able to unit test the controller. This code works in the app itself. However, I run into a problem when I attempt to unit test it. I run into issues because I can't figure out how to inject the $element into the controller from a unit test. Currently, I have the following tests setup:

describe('my.module', function () {
    var $scope;
    var myCtrl;

    beforeEach(module('myApp'));
    beforeEach(inject(function ($controller, $rootScope, $element) {
        $scope = $rootScope.$new();
        myCtrl = $controller('myCtrl', { $scope: $scope });
    }));

    it('should create controller', inject(function () {
      expect(testCtrl).toBeDefined();
    }));

    it('should create the directive', inject(function ($rootScope, $compile, $log) {
      var d = angular.element('<my-directive></my-directive>');
      $compile(d)($rootScope);
      $scope.$digest();

      expect($log.assertEmpty).not.toThrow();
    }));
});

$element is something that automatically gets injected into a directive. However, I can't figure out how to inject this into a controller. I need to do this so I can unit test it. How do I do this?

Thank you!

2 Answers 2

2

What I can recommend in unit testing of controllers is to only worry about the logic that the controller exposes. You are trying to test the logic of the directive in the controller test i.e. through $element.

Mock this in the controller test, as element can be anything. Your controller shouldn't care about the DOM; it's a controller and it's main (maybe only) job is to create a scope and expose "things" to the scope - functions, variables, logic. They shouldn't be bound to the logic of the directive or else the coupling is too tight. The controller should freely be able to receive input, correct input that is, and work with the input to give you something back as output.

If you can give an example of the logic your controller exposes then I can help further in composing the unit tests.

Update:

Now looking at the unit test you have provided. You need to separate the logic out of the unit test for the controller and directive.

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

Comments

1

for inject $element and $log do as it :

describe('my.module', function ($compile) {
    var $scope, $controller, $element, $log, $compile, html;

    beforeEach(module('myApp'));
    // an another module ...

    beforeEach(inject(function ($injector) {
       $scope = $injector.get('$rootScope');
       $controller = $injector.get('$controller');
       $element = $injector.get('$element');
       $log = $injector.get('$log');
       $compile = $injector.get('$compile');

       function createController (){
          return $controller('MyCtrl', {'$scope' : $scope, '$element' : $element, '$log': $log });
       }
       // init your controller
       createController();

    }));

    it('should create the directive',function () {
    // possibility to use $scope, $element, $log in your test
      $compile('<my-directive></my-directive>')($scope);
      $scope.$digest();

      expect($log.assertEmpty).not.toThrow();
    });
});

2 Comments

When I try the approach you've shown, I receive an error that says: ' Unknown provider: $elementProvider <- $element'. Do you by any chance have a fiddle I can try? For the life of me, I can't figure this out.
$element is a parameter of your controller, I just picked up the code I've seen, but apparently it is not used, so you can remove the injector and in the function createController.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.