Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Unit testing 如何使用$httpBackend对angularjs承诺链进行单元测试_Unit Testing_Angularjs_Jasmine_Promise - Fatal编程技术网

Unit testing 如何使用$httpBackend对angularjs承诺链进行单元测试

Unit testing 如何使用$httpBackend对angularjs承诺链进行单元测试,unit-testing,angularjs,jasmine,promise,Unit Testing,Angularjs,Jasmine,Promise,使用AngularJS,我试图对一个函数进行单元测试,该函数对$http进行多次调用 我的测试如下所示: it('traverses over a hierarchical structure over multiple chained calls', function() { myService.traverseTheStuff() .then(function(theAggregateResult) { // ...is never fulfilled

使用AngularJS,我试图对一个函数进行单元测试,该函数对$http进行多次调用

我的测试如下所示:

it('traverses over a hierarchical structure over multiple chained calls', function() {

    myService.traverseTheStuff()
    .then(function(theAggregateResult) {
        // ...is never fulfilled
    });

    $httpBackend.flush();
});
其他单次调用测试将注册传递给.then()的回调,并在调用.flush()后立即执行它

测试中的代码如下所示

function traverseTheStuff(){

    // This will make a call to $http to fetch some data
    return getRootData()

    // It is fulfilled at the end of the test when I $httpBackend.flush()
    .then(function(rootData){

        // Another call to $http happens AFTER $httpBackend.flush()
        return getNextLevel(rootData.someReference);
    })

    // The second promise is never fulfilled and the test fails
    .then(function(nextLevel){
        return aggregateTheStuff(...);
    });
}

不管它的价值是什么,每个单独的调用都是单独进行单元测试的。在这里,我想遍历一棵树,聚合一些数据并进行单元测试a)承诺链接是否正确连接,b)聚合是否准确。将其扁平化为单独的离散调用已经完成。

我是测试Angular的初学者,但我已经设置了一个plnkr,通过一个成功的“秒”then/PROMITE调用来测试与您非常相似的设置

下面的代码片段是上述plnkr的稍微简化版本

我发现的关键点是

  • 我注意到函数traverseTheStuff根本不调用$http/$httpBackend。它只使用$q承诺中定义的函数,因此对的测试假设使用$q,并注入该函数

    var deferred1 = null;
    var deferred2 = null;
    var $q = null;
    
    beforeEach(function() {
      inject(function(_$q_) {
        $q = _$q_;
      });
    });
    
    beforeEach(function() {
      deferred1 = $q.defer();
      deferred2 = $q.defer();
    }
    
  • 异步调用的函数使用它们的承诺返回值进行监视/存根,承诺是在测试本身中创建的,因此在测试时不会调用它们的实际实现

    spyOn(MyService,'traverseTheStuff').andCallThrough();
    spyOn(MyService,'getRootData').andReturn(deferred1.promise);
    spyOn(MyService,'getNextLevel').andReturn(deferred2.promise);
    spyOn(MyService,'aggregateTheStuff');
    
  • 在测试中没有任何对“then”的调用,只是对在测试中创建的承诺进行“解析”,后面跟着$rootScope.$apply(),然后实际调用traverseTheStuff中的“then”回调,我们也可以对其进行测试

    beforeEach(function() {
      spyOn(deferred1.promise, 'then').andCallThrough();
    });
    
    beforeEach(function() {
      deferred1.resolve(testData);
      $rootScope.$apply(); // Forces $q.promise then callbacks to be called
    });
    
    it('should call the then function of the promise1', function () { 
      expect(deferred1.promise.then).toHaveBeenCalled();
    });
    
  • 必须解析每个承诺/$apply ed以调用链中的下一个“then”函数。所以要让测试调用aggregateTheStuff(或者更确切地说,它的存根),还必须解析从getNextLevel存根返回的第二个承诺:

    beforeEach(function() {
      deferred2.resolve(testLevel);
      $rootScope.$apply(); // Forces $q.promise then callbacks to be called
    });
    
    it('should call aggregateTheStuff with ' + testLevel, function () {
      expect(MyService.aggregateTheStuff).toHaveBeenCalledWith(testLevel);
    });
    

以上所有内容的一个问题是,它假设$q和$rootScope中存在某些行为。我理解像这样的单元测试不应该做出这样的假设,以便真正只测试一位代码。我还没有想好如何避开这个问题,或者如果我有误解。

非常感谢!这看起来很有希望。我还刚刚开始学习angular、jasmine和测试javascript。今晚我将阅读有关spyOn的文章,并消化你的解决方案。