Jasmine:超时 - 在超时内未调用异步回调

时间:2016-04-27 06:43:15

标签: javascript angularjs unit-testing karma-jasmine

我需要测试一个从url加载图像的AngularJs服务。这是我的服务:

/*global angular, Image*/
(function () {
  'use strict';
  function SourceLoader($q, $log) {

    /**
     * Load an image from url and return a promise which is resolved when image is loading is done.
     * It return the images object as resolved promise.
     * @param url source of the image
     * @returns {Promise} unresolved promise of image.
     */
    function loadImage(url) {
      var deferred = $q.defer(),
          image = new Image();
      image.onload = function () {
        deferred.resolve(image);
      };
      image.onerror = function (error) {
        $log.error('Error while loading image from url ' + url);
        $log.debug(error);
        deferred.reject(error);
      };
      image.src = url;
      return deferred.promise;
    }

    /**
     * Load images from and array of urls nd return the array of loaded images. The order of returned
     * images is same as the order of passed urls.
     * @param urls sources if images in array.
     * @returns {Promise} unresolved promise.
     */
    function loadImages(urls) {
      var count = 0, deferred = $q.defer(),
          images = [],
          callback = function (image, index) {
            images.insert(index, image);
            count += 1;
            if (count === urls.length) {
              //All images are loaded. High time to resolve promise.
              deferred.resolve(images);
            }
          };
      urls.forEach(function (url, index) {
        var image = new Image();
        image.onload = function () {
          callback(image, index);
        };
        image.onerror = function (event) {
          $log.error('Error while loading image from url ' + url);
          $log.debug(event);
          callback(event, index);
        };
        image.src = url;
      });
      return deferred.promise;
    }

    return {
      loadImage: loadImage,
      loadImages: loadImages
    };
  }

  var app = angular.module('myCanvasModule'),
      requires = [
        '$q',
        '$log',
        SourceLoader
      ];
  app.factory('SourceLoaderService', requires);
}());

我正在使用带有Jasmine的Karma来测试套装。我的测试套装是这样的:

/*global describe, TestUtils, beforeEach, it, module, inject, SourceLoaderServiceTestData, done, expect*/
(function () {
  'use strict';
  describe('myCanvasModule: Services: SourceLoaderService-->\n\t', function () {
    var SourceLoaderService, getTestData;

    getTestData = TestUtils.getTestData.bind(null, SourceLoaderServiceTestData);

    beforeEach(function () {
      module('myCanvasModule');
    });

    beforeEach(function () {
      inject(function ($injector) {
        SourceLoaderService = $injector.get('SourceLoaderService');
      });
    });

    describe('SourceLoaderService.loadImage-->\n\t', function () {
      var testData;
      beforeEach(function () {
        testData = getTestData('loadImage');
      });

      it('should load the source and return the image as response', function (done) {
        var image;
        SourceLoaderService.loadImage(testData.validSource).then(function (response) {
          image = response;
          done();
          console.log('called');
        },function (error) {
          console.log('called', error);
          done();
        });
      });

    });

  });
}());

当我运行测试服时,我得到:

错误:超时 - 在jasmine.DEFAULT_TIMEOUT_INTERVAL指定的超时时间内未调用异步回调。

我不知道出了什么问题。我是Karma和Jasmine的新手。谢谢你的帮助。

5 个答案:

答案 0 :(得分:2)

要测试AngularJS中$ q提供程序的任何用法,您必须使摘要周期允许任何Promise在引擎盖下解析。您应该尝试以下方法:

it('should load the source and return the image as response', function (done) {
  var image;

  SourceLoaderService.loadImage(testData.validSource)
    .then(function (response) {
      // ... success check
    },function (error) {
      // ... error check
    });

  // Call apply for the above Promise to resolve
  $scope.$apply();
}

修改

在回复评论时,您还需要创建$ scope变量,如下所示:

var SourceLoaderService, $scope;

beforeEach(function () {
  inject(function ($injector) {
    SourceLoaderService = $injector.get('SourceLoaderService');
    $scope = $injector.get('$rootScope').$new();
  });
});

答案 1 :(得分:2)

我能够重现这一点,并且能够成功运行您的测试用例。我不确定你为什么要做那件事,但是下面的改变对我有用。

it('should load the source and return the image as response', function () {
  var image;
  sourceLoader.loadImage('https://s0.2mdn.net/viewad/5406241/6-jira-software_therapy-agile-tools_free-trial_static_728x90.png').then(function (response) {
    image = response;
    //setTimeout(done(), 60000);
    console.log('called');
  },function (error) {
    console.log('called', error);
    //done();
  });
});

答案 2 :(得分:2)

在你的" it" function将导致测试执行异步调用。

it('should load the source and return the image as response', function () {
  var image;
  sourceLoader.loadImage(testData.validSource).then(function (response) {
    image = response;
    console.log('called');
  },function (error) {
    console.log('called', error);
  });
});

为了使用" done"回调测试异步调用,你应该将一些代码移到" beforeEach"功能。这里有link供参考。

答案 3 :(得分:1)

图像加载可能比默认超时间隔花费更多时间 尝试这样的事情:
jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000。 它设置等待done()呼叫到30秒的时间。

答案 4 :(得分:1)

Array.insert不是一个函数。您可以扩展Array以获得插入功能。但是有一种更简单的方法。只需调用图像[:N] =图像或只使用Promise.all

这将简化您的loadImages功能:

var loadImages = function() {
  var loadImagePromises = urls.map(function(url) {
    return loadImage(url)
  })
  return Promise.all(loadImagePromises)
}