Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit test for nested controllers

I'm trying to write a unit test for a controller that's nested, but can't figure out how to mock the same behaviour in my test.

I have 2 controllers:

function FirstController ($scope) {
    $scope.childs = [{
         title : 'Hello, earth!'
    }];
};

function SecondController ($scope) {
    $scope.child.title = $scope.child.title + $scope.$index;
};

And in my HTML:

<div data-ng-controller="FirstController">
    <div data-ng-repeat="child in childs" data-ng-controller="SecondController">
        {{ child.title }}
    </div>
</div>

And this works as expected (http://jsfiddle.net/tcayp/1/)

The unittests:

// FirstController
it('Should have childs', function () {
    scope = {};
    ctrl = new FirstController(scope);
    expect(scope.childs.length).toBeGreaterThan(0);
});
// SecondController
it('Should have inherited a child', function () {
    scope = {};
    ctrl = new SecondController(scope);
    expect(scope.child.title).toEqual('Hello, earth!0');
});

In the SecondController-test I can't figure out how to mock the inherit chain from ng-repeat.

like image 547
fredrik Avatar asked Sep 18 '12 09:09

fredrik


People also ask

How will you unit test a controller?

Unit tests of controller logic. Unit tests involve testing a part of an app in isolation from its infrastructure and dependencies. When unit testing controller logic, only the contents of a single action are tested, not the behavior of its dependencies or of the framework itself.


2 Answers

Ideally, with unit tests we would like to tests classes (units) in isolation. Testing 2 controller in one test might be too much: a test would become more complex and more brittle.

Taking a closer look at the provided example one might notice that it is really not about testing 2 controllers but rather making sure that data are available in a parent scope. So, focusing on one controller only (SecondController) and the inherited data one would write a test like this:

describe('Testing the SecondController controller', function() {

    var $parentScope, $scope, ctrl;
    it('should prepare title', inject(function($rootScope, $controller) {

        //setup hierarchy of scopes with data             
        $rootScope.childs = [{
            title : 'Hello, earth!'
        }];
        $scope = $rootScope.$new();
        $scope.$index = 1;

        ctrl = $controller('SecondController', {
            $scope: $scope
        });

        expect($scope.childs[0].title).toEqual('Hello, earth!1');        
    }));
});

Here is the full jsFiddle: http://jsfiddle.net/pkozlowski_opensource/h8xry/13/

I would really advise against testing 2 controllers together but just for the sake of answering the question, it is possible as well:

describe('Testing the SecondController controller', function() {

    it('should prepare title', inject(function($rootScope, $controller) {

        $controller('FirstController', {
            $scope: $rootScope
        });

        var $scope = $rootScope.$new();
        $scope.$index = 1;

        ctrl = $controller('SecondController', {
            $scope: $scope
        });

        expect($scope.childs[0].title).toEqual('Hello, earth!1');        
    }));
});

And the jsFiddle: http://jsfiddle.net/pkozlowski_opensource/4Qy6b/1/

like image 107
pkozlowski.opensource Avatar answered Oct 13 '22 07:10

pkozlowski.opensource


AngularJS documentation suggests testing nested controllers by instantiating each of them and establishing the same scope hierarchy between them as in your app. This makes sense because (up to a point) you want to test your controller in a realistic context.

like image 35
Rod Glover Avatar answered Oct 13 '22 05:10

Rod Glover