Sinon.JS - How can I get arguments from a stub?
Asked Answered
C

1

31

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?

Cocoon answered 22/2, 2017 at 20:35 Comment(2)
Have you tried bootboxAlertStub.getCall(0).args?Essay
To clarify: the argument passed to getCall is the "index" of the call; 0 is the first call to your stub, 1 is the second call, etc.Essay
C
42

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.

Cocoon answered 22/2, 2017 at 21:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.