【问题标题】:Angular & Jasmine: how to test that a $q promise chain was resolvedAngular & Jasmine:如何测试 $q 承诺链是否已解决
【发布时间】:2015-09-22 15:46:10
【问题描述】:

我有一个服务,它公开了一个函数,该函数接收已解析的 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() 并且我也没有调用真正的异步调用(仅模拟,$q.all 除外),但承诺没有解决。 我怎样才能让它发挥作用?

【问题讨论】:

  • 注意:此代码工作正常,我可以浏览 csv,将其解析为项目并自动填充它,问题在于测试功能。我试图只留下相关代码,因此可能会出现小的复制粘贴错误。
  • 我认为您自己的角度服务与尝试测试和在线解析服务之间没有任何关联
  • @yarons 根据提供的代码,答案是“否”
  • 我不小心删除了我上面的评论。您是否将您的工厂注入到测试脚本中?

标签: angularjs jasmine promise angular-promise


【解决方案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

【讨论】:

  • 我没有复制所有代码,因为我认为它无关紧要:Papa- 是我在问题中提到的 papaparse 库,getCsvErrors - 也不相关,因为我专注于解析过程。完整代码将 csv 正确解析为项目
  • @AdiV 发布您知道不正确的代码并不是提出问题的好方法。
  • @Michael 这个 Plunker 不可用。
【解决方案2】:

看完这篇文章:https://gist.github.com/domenic/3889970我发现了我的问题。
关键是使用 promise.then 返回值来扁平化承诺链。

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

不是在内部promise成功/失败回调中解析外部promise,而是在内部promise.then回调中解析外部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();});

【讨论】:

    猜你喜欢
    • 2017-08-02
    • 1970-01-01
    • 2017-03-18
    • 2013-04-25
    • 1970-01-01
    • 2022-06-10
    • 1970-01-01
    • 1970-01-01
    • 2015-08-31
    相关资源
    最近更新 更多