AngularJS - 单元测试文件上传

时间:2013-09-25 10:56:40

标签: unit-testing testing angularjs mocking

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

最近,我遇到了在文件上传的情况下嘲笑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);

但在这种情况下,我不知道如何实现'progress'回调和'abort'回调(它们是必不可少的,并且在我现在正在处理的情况下是必需的)。

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

任何想法或者你之前遇到类似的东西?

2 个答案:

答案 0 :(得分: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);

从那里,您可以让不同的间谍返回您想要的不同测试。

答案 1 :(得分: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原则,您最好为每个回调(失败,成功和进度)设置一个测试用例。