使用Angular / Jasmine测试两个已解决的promise

时间:2017-01-17 23:08:06

标签: angularjs jasmine karma-jasmine jasmine2.0

我有一个名为myHttp的服务,它返回一个承诺,以及一个使用不同参数调用myHttp两次的控制器。

为了测试控制器,我试图用Jasmine spyOn模仿myHttp,如下:

  beforeEach(inject(function($controller, _$rootScope_, _$q_, myHttp) {
    $scope = _$rootScope_.$new();
    $q = _$q_;
    deferred = $q.defer();
    deferred2 = $q.defer();
    spyOn(myHttp, 'call').and.callFake(fake(1)).and.returnValue(deferred.promise);
    spyOn(myHttp, 'call').and.callFake(fake(2)).and.returnValue(deferred2.promise);

其中fake是一个检索myHttp调用中使用的参数的函数。

问题是我不能声明两次相同的模拟函数。我从Jasmine得到以下错误:

  

错误:呼叫已被监视

如何修复此测试?这是PLUNK

使用Javascript:

angular.module("mymodule", [])

.service('myHttp', function($http,$q){
    this.call = function(obj) {
        var defer = $q.defer();
        $http({url:obj.url, data:obj.data})
              .then(function (response) {
                  defer.resolve(response);
              });
        return defer.promise;
    };
})

.controller('ctl', function($scope,myHttp) {
      $scope.read = function (id){
          var data = {};
          data.id = id;
          myHttp.call({url:'/getStudent', data:data})
              .then(function(response) {
                  $scope.id = response.id;
                  $scope.name = response.nm;
                  $scope.classId = response.clsid; 

                  var data2 = {};
                  data2.id = $scope.classId;
                  myHttp.call({url:'/getClass', data:data2})
                      .then(function(response) {
                          $scope.className = response.nm;
                  });
              });
     };
});



describe('Testing a Controller that uses a Promise', function () {
  var $scope;
  var $q;
  var deferred;
  var $timeout;

  beforeEach(module('mymodule'));

  beforeEach(inject(function($controller, _$rootScope_, _$q_, myHttp) {
    $scope = _$rootScope_.$new();
    $q = _$q_;
    deferred = $q.defer();
    deferred2 = $q.defer();
    spyOn(myHttp, 'call').and.callFake(fake(1)).and.returnValue(deferred.promise);
    spyOn(myHttp, 'call').and.callFake(fake(2)).and.returnValue(deferred2.promise);

    $controller('ctl', { 
      $scope: $scope, 
      myHttp: myHttp
    });

    $scope.read(1)

  }));

  function fake (option) {
    if (option==1)
       return {url:'/getStudent', data: {id: 1}};
    else
       return {url:'/getClass', data: {id: 10}};
  }

  it('should resolve two promises', function () {

    var student = {
      id: 1,
      nm: "John",
      clsid: 10
    };

    var clazz = {
      id: 10,
      nm: "Math"
    };

    deferred.resolve(student);
    $scope.$apply();

    deferred2.resolve(clazz);
    $scope.$apply();

    expect($scope.id).toBe(student.id);
    expect($scope.name).toBe(student.nm);
    expect($scope.classId).toBe(student.clsid);
    expect($scope.className).toBe(clazz.nm);
  });

});

2 个答案:

答案 0 :(得分:1)

只需监视stat(2)一次并更改您的假函数调用以接收请求参数并根据请求返回一个对象。

myHttp.call

有关完整的工作示例,请参阅plunker

作为一方,您的http电话会缩短为

var student = {
  id: 1,
  nm: "John",
  clsid: 10
};

var clazz = {
  id: 10,
  nm: "Math"
};

spyOn(myHttp, 'call').and.callFake(function(obj) {
  if (obj.url == '/getStudent') {
    return $q.when(student);
  } else if (obj.url = '/getClass') {
    return $q.when(clazz);
  }
  return $q.reject('Mock not supported');
});

因为.service('myHttp', function($http,$q){ this.call = function(obj) { return $http({url:obj.url, data:obj.data}); }; }) 已经返回了一个承诺。

此外,您还可以使用角度$httpBackend来模拟您的http调用,而不是使用jasmine spyon。请参阅使用$http的{​​{3}},$httpBackend略有变化。

答案 1 :(得分:0)

非常普遍的答案是,您不必在每个人的顶级创建间谍。您可以使用更接近单个规范的beforeEach,甚至可以在it语句中创建间谍。您还可以检查假货中的某些内容,以根据伪造的参数来查看您要返回的承诺。

但是,您实际想要检查的条件是什么?通常在承诺解决后应该发生一些事情,而你实际上想要看一下。如果您说出最终目标是什么,而不是说出您认为正确的解决方案并告诉我们如何实现这一目标,那么向您提供具体的反馈会更容易。

另请参阅$q.defer: You're doing it wrong