2

I'm having trouble getting the basics of testing my code using sinon. I have a simple module that calls two internal functions and checks their result. I was to use a stub to vary the output of these functions to test how the module responds. Is this possible?

scripts.js

let check1 = function(){return true}
let check2 = function(){return true}
let startFunc = function(){console.log('checks Passed')}
let sendLog = function(message){console.log(message)}

module.exports.check=function(){

    console.log('check1: ', check1())
    console.log('check2: ', check2())

    if (!check1() || !check2()) {
        startFunc()
        sendLog("started")
    }
}

test.js:

"use strict";
var chai = require("chai");
var sinon = require("sinon");
var sinonChai = require("sinon-chai");
var expect = chai.expect;
chai.use(sinonChai);

var scripts = require('./scripts')

describe("scripts", () => {
    describe("check", () => {
        it("should startFunc and send Log if check1 || check2 is false", () => {
            var check1 = sinon.stub(check1);
            check1.yields(false);
            var check2 = sinon.stub(check2);
            check2.yields(true);
            var startFunc = sinon.stub(startFunc);
            var sendLog = sinon.stub(sendLog);
            scripts.check();
            expect(sendLog).to.have.been.calledWith("started")
            expect(startFunc).to.have.been.calledOnce;
        })
    })
})

edit:

I have managed to get the test to work by making the function accessible. I'm still not sure this is the best method though

scripts.js

let check1 = function(){return true}
let check2 = function(){return true}
let startFunc = function(){console.log('checks Passed')}
let sendLog = function(message){console.log(message)}

module.exports.check1 = check1
module.exports.check2 = check2
module.exports.startFunc = startFunc
module.exports.sendLog = sendLog

module.exports.check=function(){

    console.log('check1: ', this.check1())
    console.log('check2: ', this.check2())

    if (!this.check1() || !this.check2()) {
        this.startFunc()
        this.sendLog("started")
    }
}

test.js:

"use strict";
var chai = require("chai");
var sinon = require("sinon");
var sinonChai = require("sinon-chai");
var expect = chai.expect;
chai.use(sinonChai);

var scripts = require('./scripts')

describe("scripts", () => {
    describe("check", () => {
        it("should startFunc and send Log if check1 || check2 is false", () => {
            var check1 = sinon.stub(scripts,'check1',() => {return true});
            var check2 = sinon.stub(scripts,'check2',()=> {return false});
            var startFunc = sinon.stub(scripts,'startFunc');
            var sendLog = sinon.stub(scripts,'sendLog');
            scripts.check();
            expect(sendLog).to.have.been.calledWith("started")
            expect(startFunc).to.have.been.calledOnce;
        })
    })
})
Steve
  • 21
  • 3
  • I'd argue that exposing private methods for the sake of testing is a bad pattern. From "Pragmatic Unit Testing": *In general, you don't want to break any encapsulation for the sake of testing (or as Mom used to say, "don't expose your privates!"). Most of the time, you should be able to test a class by exercising its public methods. If there is significant functionality that is hidden behind private or protected access, that might be a warning sign that there's another class in there struggling to get out.* – sgtdck Feb 28 '17 at 15:52

1 Answers1

0

I'd just expose the internal functions from the module (as you did in your edit above). However, I'd use some special notation, e.g. _check1 and _check2, or organization of code, e.g. module.exports.privateFunctions = { check1: check1, ... };, to denote that these are internal functions not part of the intended module interface.

Another approach, although I'm not sure whether that is a good practice, would be to extend the exported methods by adding the internal functions as their properties, e.g. module.exports.check.check1 = check1;. This way each exported function will have attached references to its internally used functions whose behavior can be stubbed. Moreover the interface is safe, i.e. even if you redefine check.check1 = function() { return 42; }; in some other part of the code, this would not affect check as it will invoke the original check1.

Radko Dinev
  • 865
  • 7
  • 15