Angularjs 使用Jasmine测试Angular Controller对承诺返回服务的调用的最佳方法

Angularjs 使用Jasmine测试Angular Controller对承诺返回服务的调用的最佳方法,angularjs,testing,mocking,jasmine,promise,Angularjs,Testing,Mocking,Jasmine,Promise,在寻找一个好答案/样本一周后,我决定发布我的问题 我需要知道如何编写和测试以下内容的最佳方法: 控制器 // my.controller.js (function () { 'use strict'; angular.module('myApp.myModule').controller('Awesome', Awesome); function Awesome($http, $state, AwesomeService) { var vm = this; // us

在寻找一个好答案/样本一周后,我决定发布我的问题

我需要知道如何编写和测试以下内容的最佳方法:

控制器

// my.controller.js
(function () {

  'use strict';

  angular.module('myApp.myModule').controller('Awesome', Awesome);

  function Awesome($http, $state, AwesomeService) {

    var vm = this; // using 'controllerAs' style

    vm.init = init;
    vm.awesomeThingToDo = awesomeThingToDo;

    vm.init();

    function awesomeThingToDo() {
      AwesomeService.awesomeThingToDo().then(function (data) {
        vm.awesomeMessage = data.awesomeMessage;
      });
    }

    function init() {
        vm.awesomeThingToDo(); // Should be ready on page start
    }
  }
})();
服务

// my.service.js
(function () {
  'use strict';

  angular.module('myApp.myModule').factory('AwesomeService', AwesomeService);

  function AwesomeService($resource, $http) {

    var service = {
      awesomeThingToDo: awesomeThingToDo
    }

    return service;

    function awesomeThingToDo() {

      var promise = $http.get("/my-backend/api/awesome").then(function (response) {
          return response.data;
        });

      return promise;
    }
  }
})();
我的应用程序可以使用这种结构。我的服务单元测试也可以。 但我不知道如何在控制器上进行单元测试

我试过这样的方法:

规格


不,我不会这样做的

首先,不需要创建模拟服务:您可以注入真实的服务,并监视它

其次,Angular拥有创建承诺和解决承诺所需的一切。无需使用伪
then()
函数创建伪对象

我会这样做:

describe("Awesome Controller Tests", function() {

    beforeEach(module('myApp.myModule'));

    var vm, awesomeService, $q, $rootScope;

    beforeEach(inject(function($controller, _awesomeService_, _$q_, _$rootScope_) {
      $q = _$q_;
      awesomeService = _awesomeService_;
      $rootScope = _$rootScope_;
      vm = $controller('Awesome');
    }));

    it("Should return an awesome message", function () {
      spyOn(awesomeService, "awesomeThingToDo").and.returnValue(
          $q.when({
            awesomeMessage: 'awesome message'
          }));

      vm.awesomeThingToDo(); 

      // at this time, the then() callback hasn't been called yet: 
      // it's called at the next digest loop, that we will trigger

      $rootScope.$apply();

      // now the then() callback should have been called and initialized 
      // the message in the controller with the message of the promise
      // returned by the service

      expect(vm.awesomeMessage).toBe('awesome message');
    });

});

无关注释:1.2.28相当旧。您应该迁移到最新版本。

我用一个可能的(错误的)解决方案更新了这个问题:

it("Should return an awesome message", function () {
  // Solved with callback parameter
  spyOn(awesomeServiceMock, "awesomeThingToDo").and.callFake(function(callback) {
    return {
      then: function(callback) {
        callback({awesomeMessage: 'It is awesome!'}); //callback call works fine! :D
      }
    }
  });

我使用回调传递模拟参数并调用实际实现:D

我会更新角度版本。。。但我现在不能做(但我用解决方案更新了问题。:D谢谢!你的解决方案太冗长,而且不是最优的:承诺应该是异步的,但你的测试使它同步。你应该更喜欢我在回答中解释的基于承诺的解决方案。你是对的。我的解决方案不够好。我尝试应用你的解决方案,注入服务,使用$q…但我收到了以下错误:错误:意外请求:GET/my backend/api/awesome这意味着要么你没有发现服务方法,要么在发现服务方法之前调用了服务方法。如果你发布的是真实代码,而你使用的是我的测试代码,我看不出这是怎么回事。如果没有,那么发布您尝试的实际代码。这只是一个示例代码。实际代码有一点不同:一些控制器方法在
init()
上被调用。其中一些方法调用服务方法。问题再次更新。;)
describe("Awesome Controller Tests", function() {

    beforeEach(module('myApp.myModule'));

    var vm, awesomeService, $q, $rootScope;

    beforeEach(inject(function($controller, _awesomeService_, _$q_, _$rootScope_) {
      $q = _$q_;
      awesomeService = _awesomeService_;
      $rootScope = _$rootScope_;
      vm = $controller('Awesome');
    }));

    it("Should return an awesome message", function () {
      spyOn(awesomeService, "awesomeThingToDo").and.returnValue(
          $q.when({
            awesomeMessage: 'awesome message'
          }));

      vm.awesomeThingToDo(); 

      // at this time, the then() callback hasn't been called yet: 
      // it's called at the next digest loop, that we will trigger

      $rootScope.$apply();

      // now the then() callback should have been called and initialized 
      // the message in the controller with the message of the promise
      // returned by the service

      expect(vm.awesomeMessage).toBe('awesome message');
    });

});
it("Should return an awesome message", function () {
  // Solved with callback parameter
  spyOn(awesomeServiceMock, "awesomeThingToDo").and.callFake(function(callback) {
    return {
      then: function(callback) {
        callback({awesomeMessage: 'It is awesome!'}); //callback call works fine! :D
      }
    }
  });