【问题标题】:How to test an Image onload function within an $observe in karma如何在 karma 中的 $observe 中测试图像加载功能
【发布时间】:2017-01-05 17:11:32
【问题描述】:

我有以下指令告诉我我尝试使用的图像是否已成功加载:

return {
    restrict: 'A',
    scope: {
        imageLoad: '@'
    },
    link: function(scope, element, attrs) {

        attrs.$observe('imageLoad', function (url) {
            var deferred = $q.defer(),
                image = new Image();

            image.onerror = function () {
                deferred.resolve(false);
            };

            image.onload = function () {
                deferred.resolve(true);
            };

            image.src = url;
            return deferred.promise;
        });

    }
};

然后我想做的就是测试image.onerrorimage.onload 的两个简单测试,但我似乎只进入了错误功能,这是我目前所拥有的:

compileDirective = function() {
    var element = angular.element('<div data-image-load="http://placehold.it/350x150"></div>');
    $compile(element)(scope);
    $rootScope.$digest();
    return element;
};

beforeEach(inject(function (_$compile_, _$rootScope_) {
    $compile = _$compile_;
    $rootScope = _$rootScope_;
    scope = $rootScope.$new();
}));

it('should do something', function() {
    var compiledElement, isolatedScope;
    compiledElement = compileDirective();
    isolatedScope = compiledElement.isolateScope();
    expect(true).toBe(true);
});

显然这个测试通过了,因为它只是期望 true 为 true,但是就覆盖率而言,这进入了 onerror 函数,所以我需要以某种方式测试 deferred.promise 是否应该返回 false。

所以最终是一个两部分的问题,我如何获得 deferred.resolve 的结果?

其次,我如何进入 onload 功能?

我环顾四周,看到了一些添加以下内容的建议:

element[0].setAttribute('imageLoad','http://placehold.it/350x150');
$compile(element)(scope);
element.trigger('imageLoad');

并将data-image-load="" 留空,但似乎没有任何运气,任何建议都会很棒。

【问题讨论】:

  • 这与您的问题无关,但是您使用attrs.$observe 有什么特殊原因吗?现在您可以删除它并将url 替换为scope.imageLoad。现在传递给$observe 的匿名函数只是返回了promise,但是没有人能够看到结果。该指令的重点是什么?它应该如何使用?
  • @tasseKATT 实际应用程序中的图像路径基本上是动态的,因此可能会发生变化,因此需要重新触发。承诺确保它已加载,如果失败,我将提供后备图像,我从上面的示例中省略了一些代码,其中我使用 element.css(背景图像)设置了后备图像
  • 那么指令本身是唯一对承诺结果做出反应的指令吗?
  • 你能展示一下如何使用 promise 的结果来设置回退吗?
  • image.onerror = function () { deferred.resolve(false); element.css({ 'background-image': 'url(' + fallback + ')' }); };

标签: angularjs unit-testing angularjs-directive karma-jasmine


【解决方案1】:

从你所展示的情况来看,你根本不需要承诺。

即使你这样做了,由于 Promise 只在内部使用并且没有人使用结果,它应该被视为一个实现细节,你的测试不应该关心它。

假设您有以下指令:

app.directive('imageLoad', function() {
  return {
    restrict: 'A',
    scope: {
      imageLoad: '@'
    },
    link: function(scope, element, attrs) {

      var fallback = 'http://placekitten.com/g/200/300';

      attrs.$observe('imageLoad', function(url) {

        var image = new Image();

        image.onerror = function(e) {

          setBackground(fallback);
        };

        image.onload = function() {

          setBackground(url);
        };

        image.src = url;
      });

      function setBackground(url) {
        element.css({
          'background': 'url(' + url + ') repeat 0 0'
        });
      }
    }
  };
});

使用演示: http://plnkr.co/edit/3B8t0ivDbqOWU2YxgrlB?p=preview

从外部角度来看,该指令的目的是根据传递的 url 是否有效,将元素的背景设置为传递的 url 或后备。

所以你要测试的是:

  1. 传递的 url 正在工作 - 应该使用传递的 url 作为背景。
  2. 传递的 url 无效 - 应使用后备作为背景。

这意味着您需要能够控制图像是否可以加载。

为了防止测试中出现任何网络流量,我建议使用数据 URI 而不是 URL。

例子:

var validImage = 'data:image/jpeg;base64, + (Valid data omitted)';
var invalidImage = 'data:image/jpeg;base64,';

完整示例:

describe('myApp', function() {

  var $compile,
    $rootScope;

  beforeEach(module('myApp'));

  beforeEach(inject(function(_$compile_, _$rootScope_) {
    $compile = _$compile_;
    $rootScope = _$rootScope_;
  }));

  var validImage = 'data:image/jpeg;base64, + (Valid data omitted)';
  var invalidImage = 'data:image/jpeg;base64,';

  compileDirective = function(url) {
    var element = angular.element('<div data-image-load="' + url + '"></div>');
    return $compile(element)($rootScope.$new());
  };

  it('should use correct background when image exists', function(done) {

    var element = compileDirective(validImage);

    $rootScope.$digest();

    setTimeout(function() {

      var background = element.css('background');
      expect(background).toBe('url("' + validImage + '") 0px 0px repeat');

      done();

    }, 100);
  });

  it('should use fallback background when image does not exist', function(done) {

    var element = compileDirective(invalidImage);

    $rootScope.$digest();

    setTimeout(function() {

      var background = element.css('background');
      expect(background).toBe('url("http://placekitten.com/g/200/300") 0px 0px repeat');

      done();

    }, 100);
  });
});

请注意,由于图像的加载是异步的,因此您需要在测试中添加一些等待时间。

您可以阅读有关 Jasmine here 的更多信息。

演示: http://plnkr.co/edit/W2bvHih2PbkHFhhDNjrG?p=preview

【讨论】:

  • 谢谢你这么好的答案,显然花了很多时间来理解问题并制定解决方案,我担心我最初的方法可能不正确,这个解决方案效果很好,我特别喜欢您对 base64 编码的建议。
猜你喜欢
  • 2018-01-25
  • 2018-01-31
  • 2015-10-23
  • 1970-01-01
  • 2017-01-11
  • 1970-01-01
  • 1970-01-01
  • 2014-07-12
  • 2018-07-21
相关资源
最近更新 更多