【问题标题】:AngularJS : Testing factory that returns $http promisesAngularJS:返回 $http 承诺的测试工厂
【发布时间】:2014-09-27 09:08:25
【问题描述】:

尝试测试返回$http GET 请求和then 处理程序的角度服务,但我无法测试该逻辑在then 函数内部是否有效。这是服务代码的基本截断版本:

angular.module('app').factory('User', function ($http) {
  var User = {};

  User.get = function(id) {
    return $http.get('/api/users/' + id).then(function (response) {
      var user = response.data;
      user.customProperty = true;  
      return user;
    });
  };

  return User;
});

这是测试:

beforeEach(module('app'));
beforeEach(inject(function(_User_, _$q_, _$httpBackend_, _$rootScope_) {
  $q = _$q_;
  User = _User_;
  $httpBackend = _$httpBackend_;
  $scope = _$rootScope_.$new();
}));

afterEach(function () {
  $httpBackend.verifyNoOutstandingRequest();
  $httpBackend.verifyNoOutstandingExpectation();
});

describe('User factory', function () {

  it('gets a user and updates customProperty', function () {
    $httpBackend.expectGET('/api/users/123').respond({ id: 123 });
    User.get(123).then(function (user) {
      expect(user.customProperty).toBe(true);  // this never runs
    });

    $httpBackend.flush();
  });

});

我觉得我已经尝试了几乎所有方法来测试then 调用中的逻辑,所以如果有人可以提供建议,我将不胜感激。

编辑:我的问题也是由于不标准的注入做法,所以下面的答案在此之外起作用。

【问题讨论】:

  • 如果您在服务中执行此操作,那么您将没有应用范围。所以,我总是将承诺链的东西放在我的控制器中并在那里进行测试。这是一个类似问题的链接stackoverflow.com/a/24407839/2740086
  • 谢谢。是的,当我在控制器中测试工厂的使用时,我能够传递一个虚假的承诺,但我想测试工厂承诺内部的逻辑是否合理。这只是不好的做法吗?
  • 我所做的是:我在控制器中分离了promise的东西,所以我可以用promise.resolve()然后$scope.apply()来控制它。然后,我将成功或失败对象传递给“解析器”服务,该服务实际上会根据成功或失败执行我想要的操作。基本上,该服务有一个 getResourceFromServer() 方法和一个 resolveGetResourceFromServer(objectFromServer, successBool) 方法,我用来在服务中做任何事情。我不知道这是否是“最佳实践”,但我发现它使测试更容易一些。

标签: angularjs unit-testing jasmine karma-jasmine angular-promise


【解决方案1】:

有些东西需要改变

  • 使用whenGET 而不是expectGET 来伪造回复
  • 在测试then 回调中,将响应设置为回调外部可用的变量,以便您可以在expect 调用中对其进行测试
  • 确保expect 调用在任何回调之外,因此它始终运行并显示任何失败。

把它们放在一起:

it('gets a user and updates customProperty', function () {
  $httpBackend.whenGET('/api/users/123').respond({ id: 123 });
  User.get(123).then(function(response) {
    user = response;
  })
  $httpBackend.flush();
  expect(user.customProperty).toBe(true); 
});

可以在http://plnkr.co/edit/9zON7ALKnrKpcOVrBupQ?p=preview看到

【讨论】:

  • 您也可以使用 jasmine spyOn 和 andCallFake 工具,以您想要的状态返回一个 promise,但是,为了做到这一点,您必须将加载业务分离到一个专门的服务中。可以模拟服务,然后 spyOn 变得可用。如果你想在不使用 $httpBackend 的情况下模拟 $http,你可以通过 module(function($provide) { $provide.value('$http', yourBadMockBecauseYouAreLazy ); }) 模拟它
  • 这与我们如何注入 $http 有关吗?因为当我尝试这样做时,我仍然得到“没有待处理的刷新请求”。我们将其称为 var $http = angular.injector(['dn']).get('$http'); 以让我们的资源扩展基本资源类型。
  • @Tom 听起来您发布的代码可能不代表您的实际情况。由于现在有这个问题的答案(即我的问题!)我建议发布一个新问题,其中包含显示您的问题的代码,并带有指向 Plunker 的链接显示它工作(或者更确切地说,不工作)。
  • 很抱歉让您感到困惑,一旦我更改了服务的定义方式,您的修复确实有效。与$injector 可能返回$http 的新实例有关,但这需要单独调查。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-18
  • 2015-09-21
  • 1970-01-01
  • 2015-07-17
  • 2017-01-30
  • 2023-03-23
  • 1970-01-01
相关资源
最近更新 更多