2013-04-06 53 views
40

我有一個ParseService,我想嘲笑爲了測試所有使用它的控制器,我一直在閱讀關於茉莉花間諜,但它仍然不清楚。任何人都可以給我一個如何模擬自定義服務並在Controller測試中使用它的例子嗎?模擬一個服務,以測試一個控制器

現在我有一個使用服務來插入一本書控制器:

BookCrossingApp.controller('AddBookCtrl', function ($scope, DataService, $location) { 

    $scope.registerNewBook = function (book) { 
     DataService.registerBook(book, function (isResult, result) { 

      $scope.$apply(function() { 
       $scope.registerResult = isResult ? "Success" : result; 
      }); 
      if (isResult) { 
       //$scope.registerResult = "Success"; 
       $location.path('/main'); 
      } 
      else { 
       $scope.registerResult = "Fail!"; 
       //$location.path('/'); 
      } 

     }); 
    }; 
}); 

的服務是這樣的:

angular.module('DataServices', []) 

    /** 
    * Parse Service 
    * Use Parse.com as a back-end for the application. 
    */ 
    .factory('ParseService', function() { 
     var ParseService = { 
      name: "Parse", 

      registerBook: function registerBook(bookk, callback) { 

       var book = new Book(); 

       book.set("title", bookk.title); 
       book.set("description", bookk.Description); 
       book.set("registrationId", bookk.RegistrationId); 
       var newAcl = new Parse.ACL(Parse.User.current()); 
       newAcl.setPublicReadAccess(true); 
       book.setACL(newAcl); 

       book.save(null, { 
        success: function (book) { 
         // The object was saved successfully. 
         callback(true, null); 
        }, 
        error: function (book, error) { 
         // The save failed. 
         // error is a Parse.Error with an error code and description. 
         callback(false, error); 
        } 
       }); 
      } 
     }; 

     return ParseService; 
    }); 

而且我的測試,到目前爲止是這樣的:

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

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


    var AddBookCtrl, scope, book; 

    // Initialize the controller and a mock scope 
    beforeEach(inject(function($controller, $rootScope) { 
     scope = $rootScope; 
     book = {title: "fooTitle13"}; 
     AddBookCtrl = $controller('AddBookCtrl', { 
      $scope: scope 
     }); 
    })); 

    it('should call Parse Service method', function() { 

     //We need to get the injector from angular 
     var $injector = angular.injector([ 'DataServices' ]); 
     //We get the service from the injector that we have called 
     var mockService = $injector.get('ParseService'); 
     mockService.registerBook = jasmine.createSpy("registerBook"); 
     scope.registerNewBook(book); 
     //With this call we SPY the method registerBook of our mockservice 
     //we have to make sure that the register book have been called after the call of our Controller 
     expect(mockService.registerBook).toHaveBeenCalled(); 
    }); 
    it('Dummy test', function() { 
     expect(true).toBe(true); 
    }); 
}); 

眼下測試失敗:

Expected spy registerBook to have been called. 
    Error: Expected spy registerBook to have been called. 

我做錯了什麼?

回答

60

我做了錯事不注入嘲笑服務到在beforeEach控制器:

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

    var scope; 
    var ParseServiceMock; 
    var AddBookCtrl; 

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

    // define the mock Parse service 
    beforeEach(function() { 
     ParseServiceMock = { 
      registerBook: function(book) {}, 
      getBookRegistrationId: function() {} 
     }; 
    }); 

    // inject the required services and instantiate the controller 
    beforeEach(inject(function($rootScope, $controller) { 
     scope = $rootScope.$new(); 
     AddBookCtrl = $controller('AddBookCtrl', { 
      $scope: scope, 
      DataService: ParseServiceMock 
     }); 
    })); 

    it('should call registerBook Parse Service method', function() { 
     var book = {title: "fooTitle"} 

     spyOn(ParseServiceMock, 'registerBook').andCallThrough(); 
     //spyOn(ParseServiceMock, 'getBookRegistrationId').andCallThrough(); 
     scope.registerNewBook(book); 

     expect(ParseServiceMock.registerBook).toHaveBeenCalled(); 
     //expect(ParseServiceMock.getBookRegistrationId).toHaveBeenCalled(); 
    }); 
}); 
-2

包括angular-mocks.js在您的項目中並仔細閱讀following link

+1

的問題是,解析服務封裝的HTTP調用,所以I D沒有看到如何在我的應用程序中使用httpBackend模擬,也許我錯過了這裏的一點,我總是必須使用httpBackend來測試這種服務。 – 2013-04-08 08:19:03

+1

我認爲你的方法是好的。 httpBackend應該用於測試ParseService,而不是測試控制器。如果你嘲笑來自控制器的http調用,那麼你違反了封裝。您可以添加集成測試來測試對外部服務器的真實呼叫。 – 2014-04-02 23:19:25

11

你可以這樣注入你的服務,然後使用spyOn.and.returnValue():

beforeEach(angular.mock.module('yourModule')); 

beforeEach(angular.mock.inject(function($rootScope, $controller, ParseService) { 
    mock = { 
     $scope: $rootScope.$new(), 
     ParseService: ParseService 
    }; 
    $controller('AddBookCtrl', mock); 
})); 

it('should call Parse Service method', function() { 
    spyOn(mock.ParseService, "registerBook").and.returnValue({id: 3}); 

    mock.$scope.registerNewBook(); 

    expect(mock.ParseService.registerBook).toHaveBeenCalled(); 
}); 
1

繼Javito的answer 4年後事實。茉莉花在2.0中改變了他們的語法,通過間諜的真實方法進行調用。

變化:

spyOn(ParseServiceMock, 'registerBook').andCallThrough(); 

到:

spyOn(ParseServiceMock, 'registerBook').and.callThrough(); 

Source

相關問題