I am trying to use Sinon to test a JS component which looks a bit like this...
import Bootbox from "../helpers/bootbox";
import Guard from "../helpers/guard";
import UrlHelper from "../helpers/url-helper";
export default class DeleteButton {
/**
* Creates an instance of DeleteButton.
*
* @param {object} element The DOM element to make into a delete button.
*
* @memberOf DeleteButton
*/
constructor(element) {
Guard.throwIf(element, "element");
this.deleteUri = element.getAttribute("data-delete-uri") || UrlHelper.current.url().split('?')[0];
this.title = element.getAttribute("data-title") || `Delete the item?`;
this.cancelText = element.getAttribute("data-cancel") || `Cancel`;
this.confirmText = element.getAttribute("data-confirm") || `Remove`;
this.message = element.getAttribute("data-message") || `Do you want to delete the item? This cannot be undone.`;
this.successUri = element.getAttribute("data-success-uri");
this.errorMessage = element.getAttribute("data-error-message") || `Unable to complete operation.`;
}
/**
* Shows failure of deletion.
*
* @memberOf DeleteButton
*/
showFailed() {
Bootbox.alert({
message: this.errorMessage,
size: `small`,
backdrop: true
});
}
}
The test looks like this...
it("Can show a fail message", function() {
$("body").append(`<a class="js-delete-button" data-id="123" data-delete-uri="/delete/123">Delete</a>`);
let objUt = new DeleteButton($(".js-delete-button").get()[0]);
let bootboxAlertStub = Sinon.stub(Bootbox, 'alert');
objUt.showFailed();
let args = bootboxAlertStub.args;
expect(args.message).to.equal(objUt.errorMessage);
});
But I can't get past the line let bootboxAlertStub = Sinon.stub(Bootbox, 'alert');
because I get an error from Karma saying 'Should wrap property of object'. I've tried wrapping it up in a Sinon.test wrapper as well and using this.stub but the error the is even more obtuse.
I've been through the Sinon.JS docs and searched online but I'm stuck. The code works fine.
I have looked at this posting, which is similar - Stubbing a class method with Sinon.js but it's not quite the same.
Looking at the actual underlying bootbox JavaScript file I'm effectively trying to stub a method that looks a bit like this (cut down)...
exports.alert = function() {
// do something
};
The JS file then returns the exports at the end...
return exports;
Looking at some Github postings it seems that it may not be possible to stub these particular calls as the underlying calls are utility functions rather than object functions.
I have mitigated this (in a horrible way) by changing my Bootbox wrapper class as follows...
export default {
/**
* Generates an alert box.
*
* @param {any} options
*/
alert: function(options) {
window.bootbox.alert(options);
},
/**
* Generates a confirmation dialog.
*
* @param {any} options
*/
confirm: function(options) {
window.bootbox.confirm(options);
}
}
This solves one problem and introduces another. While I can now stub Bootbox, when stubbed I can't get the arguments....
(from the test)
let args = bootboxAlertStub.args;
This is frustrating - The arguments are passed as a complex argument so a 'calledWith' assertion isn't going to cut it.
Is there any way I can get the arguments for a stub?
The sinon. stub() substitutes the real function and returns a stub object that you can configure using methods like callsFake() . Stubs also have a callCount property that tells you how many times the stub was called.
Sinon replaces the whole request module (or part of it) during the test execution, making the stub available via require('request') and then restore it after the tests are finished? require('request') will return the same (object) reference, that was created inside the "request" module, every time it's called.
sandbox. Causes all stubs and mocks created from the sandbox to return promises using a specific Promise library instead of the global one when using stub. rejects or stub. resolves . Returns the stub to allow chaining.
To stub a promise with sinon and JavaScript, we can return a promise with a stub. import sinon from "sinon"; const sandbox = sinon. sandbox. create(); const promiseResolved = () => sandbox.
Thanks to Matt Mokary (if you want to submit an answer I'll give you the credit for this one)
I edited my test as follows...
it("Can show a fail message", Sinon.test(function() {
$("body").append(`<a class="js-delete-button" data-id="123" data-delete-uri="/delete/123">Delete</a>`);
let objUt = new DeleteButton($(".js-delete-button").get()[0]);
let bootboxAlertStub = this.stub(Bootbox, 'alert');
objUt.showFailed();
Sinon.assert.called(bootboxAlertStub);
let options = bootboxAlertStub.getCall(0).args[0];
expect(options.message).to.equal(objUt.errorMessage);
}));
The thing that fixed it was, as Matt suggested, using...
bootboxAlertStub.getCall(0);
with the slight adjustment of
bootboxAlertStub.getCall(0).args[0];
to get the first argument (which is the options object).
As an aside, discovering that I can use console.log to see what's going on when I'm running a test through Phantom and Karma was both a revelation and a headslapping moment.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With