断言函数已执行

Assert that a function was executed

我正在尝试对订阅 ko.observable 的 Knockout JS 扩展程序函数进行单元测试(当值更改时导致它变为 运行)。为了测试它是否正常工作,我需要验证扩展函数在 ko.observable 更改时是否已执行。

这是我目前的测试:

test("ko.extenders.addFieldTrackingGA", function () {

    //arrange
    var testObservable = ko.observable(1).extend({
         addFieldTrackingGA: "Some button was clicked"
    });

    //act
    testObservable(5);

    //assert

});

我的问题是:如何断言 ko.extenders.addFieldTrackingGA 在 observable 更改时执行?

这是我要确认执行的代码:

敲除扩展:

ko.extenders.addFieldTrackingGA = function (target, option) {
    target.subscribe(function (newValue) {
        if (newValue) {
            qb.Utils.Analytics().trackEvent(qb.Utils.Analytics().product,
                                            "form click",
                                            option,
                                            false);
        }
    });
    return target;
};

qb.analystics:

 /**
 * Event = e.g. 'trackEvent'
 * Category = e.g. 'error_message_home'
 * Action = fieldName
 * Label = 'some message'
 * ignoreMultiple = false | true | {blank} - if true, gtm actions that are fired more than once will be ignored, defaults to true.
 */
var _pushGTM = function (event, category, action, label, ignoreMultiple) {
    if (typeof dataLayer !== 'undefined') { // Add test for dataLayer as breaking Qunit

        ignoreMultiple = ignoreMultiple === undefined ? true : ignoreMultiple;

        if (_.contains(pushedGTM, action + label) && ignoreMultiple) { // Make sure event doesn't get fired more than once, only fire it the first time
            return;
        }

        var gtmObject = {
            'event': event,
            'eventDetails.category': category,  // Push the value depending on the form (car/house/contents)
            'eventDetails.action': action,      // Push the form field name.(If there is no field name push "No_field"
            'eventDetails.label': label         // Please push the exact error string. 
        }

        if (ignoreMultiple) {
            pushedGTM.push(action + label);
        }

        _pushGTMObject(gtmObject);
    }
}

您应该意识到您对扩展程序有相当大的依赖性:qb.analytics。目前你(副作用)也在测试,当你只想要测试一个单元:扩展器。

我可以给你至少三个基本选项来处理这个问题:

  1. 分解出 qb 上的依赖关系,并以某种方式将其注入到您的扩展器中。然后,您的测试可以注入有助于断言的模拟。
  2. 使用某种 spy/mock 框架,例如 SinonJS。我承认,由于缺乏使用 SinonJS 和类似框架的经验,我不能 100% 确定这种方法是否会奏效。
  3. 测试中的 Monkey 补丁 qb 以帮助进行断言。

后一种方法有点生硬,但很简单。它的工作原理如下:

(function() {
  "use strict";

  var trackEventFn = function() { };

  QUnit.module("Mymodule", {
    beforeEach: function() {
      window.qb = {
        Utils: {
            Analytics: function() {
              return { trackEvent: trackEventFn };
            }
        }
      };
    }
  });

  QUnit.test("ko.extenders.addFieldTrackingGA", function(assert) {
    // arrange
    var testObservable = ko.observable(1).extend({
      addFieldTrackingGA: "Some button was clicked"
    });

    // prepare assertion    
    trackEventFn = function(prod, event, option, somebool) {
      assert.ok(true);
      // You can also assert on the argument values here
    }
    assert.expect(1); // Or more than 1, depending on the above

    // act
    testObservable(5);
  });

}());

这是一个完整的演示:

window.qb = {
  Utils: {
    Analytics: function() {
      return { trackEvent: function() { } };
    }
  }
};

ko.extenders.addFieldTrackingGA = function (target, option) {
  target.subscribe(function (newValue) {
    if (newValue) {
      qb.Utils.Analytics().trackEvent(qb.Utils.Analytics().product,
                                      "form click",
                                      option,
                                      false);
    }
  });
  return target;
};

(function() {
  "use strict";

  var trackEventFn = function() { };

  QUnit.module("Mymodule", {
    beforeEach: function() {
      window.qb = {
        Utils: {
          Analytics: function() {
            return { trackEvent: trackEventFn };
          }
        }
      };
    }
  });

  QUnit.test("ko.extenders.addFieldTrackingGA", function(assert) {
    // arrange
    var testObservable = ko.observable(1).extend({
      addFieldTrackingGA: "Some button was clicked"
    });

    // prepare assertion    
    trackEventFn = function(prod, event, option, somebool) {
      assert.ok(true);
      // You can also assert on the argument values here
    }
    assert.expect(1); // Or more than 1, depending on the above

    // act
    testObservable(5);
  });

}());
<link href="https://cdnjs.cloudflare.com/ajax/libs/qunit/1.18.0/qunit.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qunit/1.18.0/qunit.min.js"></script>

<div id="qunit"></div>
<div id="qunit-fixture"></div>