Angular& Jasmine:如何测试$ q promise链是否已解决

时间:2015-07-05 11:30:37

标签: angularjs jasmine promise angular-promise

我有一个服务,它公开了一个接收解析后的CSV(使用papaparse)的函数,以及反映解析状态的promise:
 如果文件缺少必填字段,则拒绝承诺  否则,它会将每行解析为一个项目并自动填充缺少的字段(自动填充过程是异步的)  当所有项都被填充时,该函数使用items数组

解析promise

我要测试的函数是onCsvParse:

angular.module('csvParser', [])
  .factory('csvParser', ['$http',
    function($http) {
      var service = {
        onCsvParse: function(results, creatingBulkItems) {
          var errors = this.getCsvErrors(results);
          if (errors.length > 0) {
            //reject
            creatingBulkItems.reject(errors);
          } else {

            var items = this.parseCsv(results);
            var autoPopulateItems = [],
              populatedItems = [];
            for (var i = 0; i < populatedItems.length; i++) {
              var item = items[i];
              if (item.name === "" /*or some any field is missing */ ) {
                // auto populate item
                autoPopulateItems.push(this.autoPopulateItem(item));
              } else {
                var populatedItem = $q.when(item);
                populatedItems.push(populatedItem);
              }
            }
            populatedItems =autoPopulateItems.concat(populatedItems);
            var populatingAllItems = $q.all(populatedItems);
            populatingAllItems.then(function(items) {
              creatingBulkItems.resolve(items);
            }, function(err) {
              creatingBulkItems.resolve(err);
            });
          }
        },
        autoPopulateItem: function(newItem) {
          var populatingItem = $q.defer();
          var item = angular.copy(newItem);
          $http.post('api/getItemData', { /*.....*/ })
            .success(function(response) {
              //----Populate item fields
              item.name = response.name;
              //....
              //resolve the promise
              populatingItem.resolve(item)

            }).error(err) {
              // resolving on error for $q.all indication
              populatingItem.resolve(item)
            };
          return populatingItem.promise;

        }
      }
      return service;
    }
  ])

我对此方法的测试如下(简化):

describe('bulk items upload test', function() {
  //upload csv & test scenarios...
  var $rootScope, $q, csvResults = {};
  var $httpBackend, requestHandler;
  beforeEach(module('csvParser'));

  beforeEach(inject(function(_$rootScope_, _$q_) {
    $rootScope = _$rootScope_;
    $q = _$q_;
  }));
  beforeEach(inject(function($injector) {
    // Set up the mock http service responses
    $httpBackend = $injector.get('$httpBackend');
    // backend definition common for all tests
    requestHandler = $httpBackend.when('POST', 'api/getItemData')
      .respond({
        name: "name",
        description: "description",
        imageUrl: "www.google.com"

      });


    //   afterEach(function(){ $rootScope.$apply();});

  }));
  it('Should parse csv string', function(done) {
    var csvString = "Name,Description of the page";//...

    Papa.parse(csvString, {
      complete: function(results) {
        csvResults = results;
        done();
      }
    });

  });
  it('Should fail', function(done) {
    var creatingBulkItems = $q.defer();
    console.log("here..");
    csvParser.onCsvParse(csvResults, creatingBulkItems);
    creatingBulkItems.promise.then(function() {
      console.log("1here..");
      //promise is never resolved
      expect(1).toEqual(1);
      done();
    }, function() {
      //promise is never rejeceted
      console.log("2here..");
      expect(1).toEqual(1);
      done();
    });
    $rootScope.$apply();

  });

});

有了这个,我得到错误:错误:超时 - 在jasmine.DEFAULT_TIMEOUT_INTERVAL指定的超时内没有调用异步回调。

承诺没有得到解决,虽然我调用了$ rootScope。$ apply()而且我也没有调用真正的异步调用(只有mocks,除了$ q.all)。 我怎样才能使它发挥作用?

2 个答案:

答案 0 :(得分:1)

语法无效。您需要将函数传递给error回调。

        }).error(function(err) {
          // resolving on error for $q.all indication
          populatingItem.resolve(item)
        });
      return populatingItem.promise;

此外,jasime测试需要更多初始化: http://plnkr.co/edit/wjykvpwtRA0kBBh3LcX3?p=preview

答案 1 :(得分:0)

阅读本文后:https://gist.github.com/domenic/3889970我发现了我的问题 关键是使用 promise.then 返回值来压缩承诺链。

  

这个[promise.then]函数应该返回一个新的promise,当给定的fulfilledHandler或errorHandler回调完成时,它将被满足。这允许将承诺操作链接在一起。回调处理程序返回的值是返回的promise的履行值。如果回调引发错误,则返回的promise将被移至失败状态。

而不是在内部promise中成功/失败回调中解析外部promise,外部promise在内部promise中解析。然后回调

所以我的修复是这样的:

onCsvParse: function(results) {
      var errors = this.getCsvErrors(results);
      if (errors.length > 0) {
         var deferred = $q.defer();
        //reject
        return deferred.reject(errors);
      } else {

        var items = this.parseCsv(results);
        var autoPopulateItems = [],
          populatedItems = [];
        for (var i = 0; i < populatedItems.length; i++) {
          var item = items[i];
          if (item.name === "" /*or some any field is missing */ ) {
            // auto populate item
            autoPopulateItems.push(this.autoPopulateItem(item));
          } else {
            var populatedItem = $q.when(item);
            populatedItems.push(populatedItem);
          }
        }
        populatedItems = autoPopulateItems.concat(populatedItems);
        var populatingAllItems = $q.all(populatedItems);
        return populatingAllItems.then(function(items) {
            return items;
        }, function(err) {
          return err;
        });
      }
    },

测试代码:

it('Should not fail :)', function(done) {
csvParser.onCsvParse(csvResults).then(function(items) {
  //promise is resolved
  expect(items).not.toBeNull();
  done();
}, function() {
  //promise is rejeceted
  //expect(1).toEqual(1);
  done();
});
$rootScope.$apply();});