Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test elements rendered in Angular app with Jasmine tests?

This should be a very simple question, I hope... I am an Angular newbie, and a newbie to the whole process of writing tests.

This is my controller:

angular
  .module('myModule', [])
  .controller('myCtrl', ['$scope', function ($scope) {
    $scope.questionIndex = -1;
    $scope.text = "Hello world";
}]);

My view (index.html) is as follows:

<div id="text">{{ text }}</div>

This is my test, which is passing fine:

describe('Controller: myCtrl', function () {

  // load the controller's module
  beforeEach(module('myApp'));

  var MainCtrl,
    scope;

  // Initialize the controller and a mock scope
  beforeEach(inject(function ($controller, $rootScope) {
    scope = $rootScope.$new();
    MainCtrl = $controller('myCtrl', {
      $scope: scope
    });
  }));

  it('should have the initial question index set to -1', function () {
    expect(scope.questionIndex).toBe(-1);
  });

});

Now I want to write a test to check that the text element was actually rendered to the page.

How can I do this in Jasmine? Sorry, it's probably a stupid question but I can't figure it out from the documentation.

like image 670
Richard Avatar asked Nov 22 '13 00:11

Richard


Video Answer


1 Answers

I'll give you two answers to your question:

First: Testing bindings in your view should probably be done in an end-to-end (E2E) test. And end to end test is used to make sure that the controllers, models and views are working together as expected, along with properly integrating your backend (if you have one). This is where you would test that the given div has the expected text in it. You can read all about that in the developer guide here. The reason you use an E2E test for this is because the binding isn't truly a responsibility of your controller. The controller handles/manipulates the model. The model is then passed to the view, and it is the view's responsibility to then render the DOM elements using that model. The only reliable way to test your DOM elements is through an E2E test.

Second: It can actually be done in a unit test, but the way to do it isn't exactly pretty. You can do this by using angular's $compile service. This service is what angular uses to parse through the DOM and turn all bindings/directives/etc into the end product. Here's an example of how that would be done:

var scope, compile, elem;
beforeEach(inject(function ($controller, $rootScope, $compile) {
    scope = $rootScope.$new();
    compile = $compile;
    MainCtrl = $controller('myCtrl', {
       $scope: scope
    });
}));

it('should set the div content to "' + scope.text + '"', function(){
    var html = '<div id="text">{{ text }}</div>';
    elem = angular.element(html);  // turn html into an element object
    compile(elem)(scope); // compile the html
    scope.$digest();  // update the scope
    expect(elem.text()).toBe(scope.text);  //test to see if it was updated.
});

For more info on this second option, please see the detailed tutorial found here. Hope that helps.

like image 146
tennisgent Avatar answered Nov 06 '22 03:11

tennisgent