Possibly the greatest value in having tests for your code is...
Tags: node, javascript, testing, unittesting
Published: 2019-10-02 18:55:46

---
title: Possibly the greatest value in having tests for your code is...
published: true
canonical_url: https://www.coreycleary.me/possibly-the-greatest-value-in-having-tests-for-your-code-is/
tags: node,javascript,testing,unittesting
---

<em><strong>Originally published at <a href="https://www.coreycleary.me/possibly-the-greatest-value-in-having-tests-for-your-code-is/">coreycleary.me</a></strong>. This is a cross-post from my content blog. I publish new content every week or two, and you can <a href="https://www.coreycleary.me/about/">sign up to my newsletter</a> if you'd like to receive my articles directly to your inbox! I also regularly send cheatsheets and other freebies.</em>

As developers, we constantly hear that we should be writing tests...

<blockquote>"All good developers know how to write tests, and they write them!"</blockquote>

<strong>But why?</strong>

We're told this will help prove our code is correct...

Or that it will prove we've met requirements...

Or that it will allow us to make changes without worrying if we broke something...

Or that it serves as a form of documentation...

And while all of those are true (for the most part at least- your code might not be truly bug-free unless you use something like <a href="https://learntla.com/introduction/" rel="noopener" target="_blank">formal methods/verification</a>), I think possibly the greatest value in having tests for your code is in having documentation of <strong>inputs</strong> and <strong>outputs</strong> for your functions.

And then what I'd argue is definitely the best value in <strong>reading</strong> tests is in using those documented inputs and outputs to <strong>figure out how the rest of the application fits together</strong>. Because those inputs and outputs are the glue that piece the rest of the application together.

Truly grasping this value of tests will encourage you, as a developer, to not see tests as something pointless and skip writing them, but to start to enjoy it once you experience how much easier they make your life (and your teammates' lives). Most importantly, it won't feel like a chore anymore but just part of the natural developer workflow, like writing code itself.

<h3>Inputs and Outputs</h3>
So what <em>exactly</em> is meant by <strong>inputs and outputs</strong>? Let's look at an example from Node's <a href="https://github.com/expressjs/express" rel="noopener" target="_blank">Express</a> framework:

If you've ever worked with Express, you've almost definitely used the `res.send()` function. If you haven't, basically what it does is returns a response/data from your REST API.

Imagine you were working on fixing a bug in Express' code and you had to figure out how `res.send()` works under the hood. First you might go to the function definition and start to look through the code. The first few lines of code look somewhat understandable:
```javascript
res.send = function send(body) {
  var chunk = body;
  var encoding;
  var req = this.req;
  var type;

  // settings
  var app = this.app;

  // allow status / body
  if (arguments.length === 2) {
    // res.send(body, status) backwards compat
    if (typeof arguments[0] !== 'number' && typeof arguments[1] === 'number') {
      deprecate('res.send(body, status): Use res.status(status).send(body) instead');
      this.statusCode = arguments[1];
    } else {
      deprecate('res.send(status, body): Use res.status(status).send(body) instead');
      this.statusCode = arguments[0];
      chunk = arguments[1];
    }
  }
```
Basically some setup stuff going on, some backwards compatibility stuff going on, some prep for setting the encoding, etc. Even if it's not immediately apparent what the code is doing or why it's doing it, it's still just Node code and, aside from the `deprecate` method (which is fairly self-explanatory), the code is "self-contained". I.e. - no other function calls, yet, that we need to understand.

Let's go a little further down the definition:
```javascript
  // write strings in utf-8
  if (typeof chunk === 'string') {
    encoding = 'utf8';
    type = this.get('Content-Type');

    // reflect this in content-type
    if (typeof type === 'string') {
      this.set('Content-Type', setCharset(type, 'utf-8'));
    }
  }
```
Ok, we're checking the request type to see what type it is, then we're getting the HTTP `Content-Type` from the HTTP request, then... what's that `setCharset` function doing?

From the surrounding context of the code we can kinda figure out what it's doing mostly by just it's name, but how do we know what it returns? And how do we really understand the various inputs (arguments) we can call it with?

This is where reading the tests for that function comes in handy in understanding its inputs and outputs. `setCharset` is a utility function, so we can search in our IDE or editor for the `utils.js` tests:
```javascript
describe('utils.setCharset(type, charset)', function () {
  it('should do anything without type', function () {
    assert.strictEqual(utils.setCharset(), undefined);
  });

  it('should return type if not given charset', function () {
    assert.strictEqual(utils.setCharset('text/html'), 'text/html');
  });

  it('should keep charset if not given charset', function () {
    assert.strictEqual(utils.setCharset('text/html; charset=utf-8'), 'text/html; charset=utf-8');
  });

  it('should set charset', function () {
    assert.strictEqual(utils.setCharset('text/html', 'utf-8'), 'text/html; charset=utf-8');
  });

  it('should override charset', function () {
    assert.strictEqual(utils.setCharset('text/html; charset=iso-8859-1', 'utf-8'), 'text/html; charset=utf-8');
  });
});
```
As you can tell, these tests provide several different values for the inputs/arguments, from which we can also understand the output. I.e. - 

<ul>
	<li>if we don't provide any inputs, we will get 'undefined' as the output</li>
	<li>passing in an already formatted charset like `text/html; charset=utf-8` will just return the same string</li>
	<li>passing in two separate values such as `text/html` and `utf-8` will combine them into `text/html; charset=utf-8`</li>
	<li>and if we pass in a charset value in the first argument, as well as one for the second argument, the second argument takes preference, like `text/html; charset=iso-8859-1` and `utf-8`</li>
</ul>

In essence, we now understand how to <strong>call</strong> this function, and what we will <strong>get back</strong> from the function. What the function <em>does</em>...

Now, what if we wanted to look at `setCharset` under the hood?
```javascript
function setCharset(type, charset) {
  if (!type || !charset) {
    return type;
  }

  // parse type
  var parsed = contentType.parse(type);

  // set charset
  parsed.parameters.charset = charset;

  // format type
  return contentType.format(parsed);
};
```
It too, executes other functions! These, from the <a href="https://github.com/jshttp/content-type" rel="noopener" target="_blank">content-type module</a>.

We can follow the <strong>same</strong> process to get an understanding of these functions, and thus, a better understanding of `setCharset`.

I'll just show a few of the `contentType.parse` and `contentType.format` tests here:
```javascript
it('should parse basic type', function () {
  var type = contentType.parse('text/html')
  assert.strictEqual(type.type, 'text/html')
})

it('should parse with suffix', function () {
  var type = contentType.parse('image/svg+xml')
  assert.strictEqual(type.type, 'image/svg+xml')
})

it('should format basic type', function () {
  var str = contentType.format({ type: 'text/html' })
  assert.strictEqual(str, 'text/html')
})

it('should format type with suffix', function () {
  var str = contentType.format({ type: 'image/svg+xml' })
  assert.strictEqual(str, 'image/svg+xml')
})
```

<h3>The glue</h3>
Now going back to the `res.send()` function, we can now better understand why the `setCharset` function is there:
```javascript
if (typeof type === 'string') {
  this.set('Content-Type', setCharset(type, 'utf-8'));
}
```
We need to format the `Content-Type` <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type" rel="noopener" target="_blank">header</a> for our HTTP response, and `setCharset` formats that into a valid string for the header.

This is why I say inputs/outputs are the <strong>"glue"</strong> of your code. You might not understand what one function or part of a function does, but by understanding the values passed to it and what you get back/what actions that function takes, you can start to piece together your understanding of the rest of the application code.

The function chain call here is: `res.send` -> `utils.setCharset` -> `content-type.parse/format`.

At a basic level, code is functions which operate on data (inputs). After all, if you never pass in <em>anything</em> to your functions... what would your application really do? Not much. Not very usable...

And tests are the documentation of those inputs/outputs that allow you to unlock a deeper understanding of the code. Sure, some code you can read and understand without relying on reading tests. In fact, most good code should be readable in this way.

But even if you <em>can</em> immediately understand what the code does, you may not understand how it will operate with different values passed to it, like in the `should override charset` test from above.

<h3>Versus JSDoc</h3>
If you're familiar with <a href="https://devdocs.io/jsdoc/howto-es2015-modules" rel="noopener" target="_blank">JSDOC</a>, you'll know it will give us some documentation of inputs/outputs above the function definition. Usually something like this:
```javascript
  /**
   *
   * @param {Object} config - Configuration used for blah blah blah.
   *
   * @returns {Promise} A promise resolved after the config has been used to setup blah blah blah.
   */
   function configureApp(config) { /* function body */ }
```
But JSDoc, although helpful, really just gives the <strong>type</strong> of the inputs (`@param`) and output (`@returns`), with a small description. It's not the most complete documentation. Can you imagine using JSDoc to add all the input combinations from the `setCharset` tests above? It would be a <strong>massive</strong> pain.

With tests, we get a much clearer picture.

And if you found this post helpful, <a href="https://www.coreycleary.me/about/">here's that link again</a> to subscribe to my newsletter!