Using Sinon.js Spies & Stubs in Javascript Unit Testing
Published: 2020-08-31 05:12:02

Say you've been tasked with an objective and have spent a significant amount of time constructing Javascript code that you assume will result in your desired output. How can you know for sure? You could just push that code into production and wait for the bug reports to roll in, oooor you could take a more proactive approach and test as you go for any potential issues while in development.

![Coding GIF](https://media.giphy.com/media/26tn33aiTi1jkl6H6/source.gif)

If you choose this route (honestly, as you should), you'll need to familiarize yourself with automated tests. When written properly, tests can give developers an idea of where their code might break. There are a different few types of automated tests, but here we'll be touching on *unit tests*.

##**the utility of unit tests**
As budding software developers putting our newfound knowledge into practice via sprints and toy problems, we are often evaluating the efficacy of our code by way of unit tests. Just as you might imagine, these tests evaluate units, or small chunks, of code. With unit tests (as opposed to, say, functional or integrated), we're often checking if a function is running as intended.  

![Mocha test runner](https://mochajs.org/static/reporter-html.resize920,9999-withoutEnlargement.e2b1172cb9.png)

There are a *bunch* of Javascript unit test frameworks out there, but in this post we'll be touching on some of the ins and outs of [Sinon.js](https://sinonjs.org/), which offers "standalone test spies, stubs, and mocks [that also] works with any unit testing framework".

When dipping your toes into the waters of unit testing, it may be hard to conceptualize how to test code that depends on complex and dynamic external sources, such as servers or databases. We don't want to be sending requests to those servers or databases with every test we run, so it's important to figure out a way to simplify complex functions for the sake of testing.

##**... cue Sinon!**
![Sinon.js logo](https://camo.githubusercontent.com/8db2748b2b99e495d932058ec2af35f28a66af12/68747470733a2f2f73696e6f6e6a732e6f72672f6173736574732f696d616765732f6c6f676f2e706e67)

Sinon offers an answer to this issue by allowing programmers to create "test-doubles," that is ephemeral replacements for chunks of code. These test-doubles replace specific functions during test time, and can be configured in a multitude of ways. 

There are three types of test-doubles in Sinon: *spies*, *stubs*, and *mocks*. Because mocks are essentially spies and stubs packaged into one, we'll be reviewing here the contrast between those building blocks.

##**the spies and stubs of Sinon.js**
**Sinon spies** gather info about function calls. Spies can be used to verify whether or not a function was called, the number of times the function was called, the arguments that were passed in at call time, and then also the return value of that function call.

Here's an example of a Sinon spy some of you may recognize. This spy is verifying that a function, Parse.create();, is being called after a form's submit button is clicked.

```
sinon.spy(Parse, 'create');
App.initialize();
$('#message').val('Why so many Mel Brooks quotes?');
$('form .submit').trigger('submit');
expect(Parse.create.called).to.be.true;
Parse.create.restore();
```

One takeaway here is that Sinon spies rely on the original function running. **Sinon stubs**, on the other hand, go one step further by not only spying on what a function does, but also by completely replacing the original function during the test. Take our earlier example of having a block of code that is requesting information from external sources. Testing that function as it is would slow down our processing time as well as burden our external sources with unimportant data. Substituting the function with a stub would sufficiently avoid that. Here's an example from the [Sinon.js documentation] (https://sinonjs.org/releases/v9.0.3/stubs/).

```
describe("stub", function() {
    it("should behave differently on consecutive calls", function() {
        const callback = sinon.stub();
        callback.onCall(0).returns(1);
        callback.onCall(1).returns(2);
        callback.returns(3);

        assert.equals(callback(), 1); // Returns 1
        assert.equals(callback(), 2); // Returns 2
        assert.equals(callback(), 3); // All following calls return 3
    });
});

```

![JS Testing Flow Chart](https://i.ibb.co/mvwnpRT/png-transparent-unit-testing-software-testing-test-automation-javascript-apache-cordova-potential-di.png)
Of course you shall learn more about automated testing frameworks as you progress through your coding journey & encounter various projects with their unique objectives, but it is my hope that after reading this post you have deepened your understanding of the unit tests that you'll often face as a student of software development.