6

如您所知,在单元测试中,它的内置 angularjs 功能可以使用 $httpBackend 模拟 XHR 请求 - 这在编写单元测试时非常有用。

最近遇到了在文件上传的情况下mock XHR的需求,发现了一些问题。

考虑以下代码:

var xhr = new XMLHttpRequest();

xhr.upload.addEventListener("progress", uploadProgress(event), false);
xhr.addEventListener("load", uploadComplete(event), false);
xhr.addEventListener("error", uploadError(event), false);
xhr.addEventListener("abort", uploadAbort(event), false);
xhr.open("POST", 'some url');
xhr.send(someData);

我想要做的是通过模拟 XHR 请求对这样的代码进行单元测试,但这是不可能的,因为这里没有使用 $http 服务。

我试过这个(它正在工作,可以用 $httpBackend 来模拟):

$http({
    method: 'POST', 
    url: 'some url', 
    data: someData, 
    headers: {'Content-Type': undefined},
    transformRequest: angular.identity})
.then(successCallback, errorCallback);

但在这种情况下,我不知道如何实现“进度”回调和“中止”回调(如果我现在正在工作,它们是必不可少的并且是必需的)。

我已经看到最新的 Angular 支持 Promise 的进度回调的信息(不确定它是否与 $http 服务集成),但是中止回调呢?

有什么想法,或者您以前遇到过类似的事情吗?

4

2 回答 2

11

如果该$http服务没有为您提供所需的一切,您仍然可以对第一个代码块进行单元测试。首先,更改您的代码以使用 Angular 的$window服务。这只是一个包装服务,但它允许您在测试中模拟对象。所以,你会想要这样做:

var xhr = new $window.XMLHttpRequest();

然后在你的测试中,只是模拟它并使用间谍。

$window.XMLHttpRequest= angular.noop;
addEventListenerSpy = jasmine.createSpy("addEventListener");
openSpy = jasmine.createSpy("open");
sendSpy = jasmine.createSpy("send");
xhrObj = {
   upload: 
   {
       addEventListener: addEventListenerSpy
   },
   addEventListener: addEventListenerSpy,
   open: openSpy,
   send: sendSpy
};
spyOn($window, "XMLHttpRequest").andReturn(xhrObj);

从那里,你可以让不同的间谍返回你想要的任何东西来进行不同的测试。

于 2013-09-27T21:55:14.707 回答
2

您应该模拟$http和控制任何延迟,因为您希望更好地控制您的测试。基本上,模拟$http提供者并提供一个暴露其延迟的自定义实现,然后使用它。

您不应该担心是否$http工作正常,因为它应该并且已经过测试。所以你必须模拟它,只担心测试你的代码部分。

你应该这样做:

describe('Testing a Hello World controller', function() {
  beforeEach(module(function($provide) {
    $provide.provider('$http', function() {
      this.$get = function($q) {
        return function() {
          var deferred = $q.defer(),
              promise = deferred.promise;

          promise.$$deferred = deferred;
          return promise;
        }
      };
    });
  }));

  it('should answer to fail callback', inject(function(yourService, $rootScope) {
    var spyOk = jasmine.createSpy('okListener'),
        spyAbort = jasmine.createSpy('abortListener'),
        spyProgress = jasmine.createSpy('progressListener');

    var promise = yourService.upload('a-file');
    promise.then(spyOk, spyAbort, spyProgress);

    promise.$$deferred.reject('something went wrong');
    $rootScope.$apply();

    expect(spyAbort).toHaveBeenCalledWith('something went wrong');
  }));
});

您的服务很简单:

app.service('yourService', function($http) {
  return {
    upload: function(file) {
      // do something and
      return $http({...});
    }
  };
});

请注意,承诺通知仅在最新的 RC 版本中可用。因此,如果您不能使用它,只需详细说明示例并模拟 XHR 事件等。

另请注意,您最好为每个回调(失败、成功和进度)设置一个测试用例,以遵循 KISS 原则。

于 2013-09-30T14:22:54.530 回答