Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice: $rootScope or child $scope in unit tests?

I use a basic pattern in my unit tests (running with karma/jasmine) for my angular components and I do not manage to establish if my solution is safer or overkill than the one suggested by my coworkers:

TL;DR: What are the pros/cons of working with $rootScope directly in unit tests (only!)?

Here's my current pattern:

describe('component', function() {
  var $scope;

  beforeEach(module('myModule'));

  beforeEach(inject(function($rootScope) {
    $scope = $rootScope.$new();
    //working with it now
    $scope.foo = 'fooValue';
    $scope.$digest();
  }));

  afterEach(function() {
    $scope.$destroy();
  });

  describe('subcomponent', function() {
    it('should do something', function() {
      //arrange
      //act
      //assert
    });
  });
});

And my coworkers suggest that using:

$scope = $rootScope;

instead of

$scope = $rootScope.$new();

would be simpler with no side-effect since inject creates a new $injector before each spec which provides a new and clean$rootScope.

So, what could be the benefits / risks of these two solutions?

Nota bene: In our apps, we always avoid using $rootScope directly.

like image 883
glepretre Avatar asked May 20 '14 10:05

glepretre


2 Answers

Testing directly on the $rootScope should work just fine in most cases. However, if the component that you are testing injects $rootScope then the following weirdness happens:

  • If you're testing a directive, then: element.scope() === $rootScope
  • If you're testing a controller, then: $scope === $rootScope

(The above is true assuming you don't mock $rootScope). Always instantiating a child scope via $rootScope.$new() as you've been doing is a safety measure which costs little to implement.

Also keep in mind that $rootScope and $rootScope.$new() are not instances of the same class, although in practice this hardly seems to be an issue in unit testing...

The $rootScope object:

$rootScope

The child scope object created by $rootScope.$new():

$rootScope.$new()

The child isolated scope object created by $rootScope.$new(true):

$rootScope.$new(true)

like image 133
Gil Birman Avatar answered Nov 18 '22 11:11

Gil Birman


I suspect there isn't much in it. The only practical difference between the $rootScope and child $scopes that I know of, is that $parent is null on $rootScope. Using $parent can be quite brittle, and so I wouldn't recommend it.

So as a first port of call, I would use

$scope = $rootScope;

If your component depends on $parent being not null, the test should fail, and you can decide whether to change the test to use

$scope = $rootScope.$new();

or change the component to not depend on $parent.

like image 4
Michal Charemza Avatar answered Nov 18 '22 09:11

Michal Charemza