konpai
konpai

Reputation: 101

Jasmine: Test void function that contains async call

I'd like to know which would be the best way to test functions that returns nothing(just changes a field value) and contains an async call.

This the AngularJS controller I want to test, the service I call returns a promise(always returns {name:"John"}):

app.controller('MyCtrl', function($scope, AsyncService) {

  $scope.greeting = "";
  $scope.error = 

  $scope.sayHello = function() {

    AsyncService.getName().then(
        function(data){
            $scope.saludo = "Hello " + data.name;
        },
        function(data){
              $scope.error = data;
        }
    );

  };

});

This would be the spec if the sayHello function did not contain an async call, but it always fails because scope.greeting is always empty.

describe('Test My Controller', function() {

  var scope, $httpBackend;

  // Load the module with MainController
  //mock Application to allow us to inject our own dependencies
  beforeEach(angular.mock.module('app'));

  //mock the controller for the same reason and include $rootScope and $controller
  beforeEach(angular.mock.inject(function($rootScope, $controller,_$httpBackend_){

      //Mock the service to always return "John" 
      $httpBackend = _$httpBackend_;
      $httpBackend.when('POST', 'http://localhost:8080/greeting').respond({name: "John"});

      //create an empty scope
      scope = $rootScope.$new();
      //declare the controller and inject our empty scope
      $controller('MyCtrl', {$scope: scope});
  }));

  it('$scope.greeting should get filled after sayHello', function() {
    expect(scope.greeting).toEqual("");
    scope.sayHello();
    expect(scope.greeting).toEqual("Hello John");
  });*/

});

How would I make this spec to handle the async call? I don't really understand how and where to use the "done" flag of Jasmine 2.0.

Upvotes: 0

Views: 2573

Answers (1)

Wayne Ellery
Wayne Ellery

Reputation: 7958

Use $q.defer() to return a promise from the getName function in a mock of your service. Then pass the mocked into the dependancies when your controller is created:

beforeEach(inject(function($controller, _$rootScope_, $q) {
    $rootScope = _$rootScope_;
    deferred = $q.defer();

    asyncService = {
        getName: function () {
        }
    };

    spyOn(asyncService, 'getName').and.returnValue(deferred.promise);

    $scope = $rootScope.$new(); 

    createController = function() {
        return $controller('MyCtrl', { $scope: $scope, AsyncService: asyncService } );
    };
}));

Then after you call $scope.hello() call deferred.resolve(data)l where data is the data that you want returned from your service in the promise. Then call $rootScope.$digest();

it('$scope.saludo should get filled after sayHello', function() {
    //Arrange
    var controller = createController();
    var data = {
      name: 'John'
    };

    //Act
    $scope.sayHello();
    deferred.resolve(data);
    $rootScope.$digest();

    //Assert
    expect($scope.saludo).toEqual('Hello ' + data.name);
});

Plunkr

Upvotes: 1

Related Questions