I am trying to unit test a Knockout JS extender function that subscribes to a ko.observable
(causing it to run when the value changes). To test that it works correctly, I need to verify that the extender function was executed when the ko.observable
was changed.
Here is my test so far:
test("ko.extenders.addFieldTrackingGA", function () {
//arrange
var testObservable = ko.observable(1).extend({
addFieldTrackingGA: "Some button was clicked"
});
//act
testObservable(5);
//assert
});
My question is: How can I assert that ko.extenders.addFieldTrackingGA was executed when the observable was changed?
Here is the code that I want to confirm executed:
Knockoutextension:
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);
}
}
You should realize that you have a pretty heavy dependency in your extender: qb.analytics. Currently you're (by side effect) also testing that, when you only want to test one unit: the extender.
I can give you at least three basic options to handle this:
qb
and inject it somehow into your extender. Your tests can then inject mocks that help with the assertion.qb
in your tests to help with assertions.The latter approach is a bit blunt, but it's straightforward. Here's how it would work:
(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);
});
}());
Here's a full demo:
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>