Exercise: Test Driven Development With Azure Functions

Challenge

Use a unit test framework to create a test driven development (TDD) pipeline for the “Sleeps Until” Azure Function we’ve been building.

Solution

The main feature of a good unit test is that it must be fast. You need to be running the tests repeatedly while developing and if your tests are slow you won’t keep up the discipline. Fast implies that you can’t wait on a network call to run the tests; they must run locally.

The Azure team has created a set of tools to allow you to run your functions locally (here are the docs), but this is too heavyweight. We’re trying to set up unit tests. We don’t need to integrate the whole functions stack. All we need to do is run a little JavaScript code.

Working Example

Let’s try and use TDD to refactor some candidate code from the Sleepsuntil code base.

Example code to refactor:

code_to_refactor

We want to pull that into a single function, has_correct_params(), and we want to develop the test first.

We need to write a test test_has_correct_params() that asserts some things about how the code should work and then refactor out the existing code into a new function. The problem is how to have a separate test file and have that call the function under test. Azure functions won’t let us export more than the one function from a single file, see here:

All JavaScript functions must export a single function via module.exports for the runtime to find the function and run it. This function must always include a context object.

As a solution we will pull the code to refactor into a separate file, sleeps.js, and then export the functions we need back to both the test files and the Azure HTTP Trigger function.

Steps to Write the Code

We’re going to need a unit testing framework to build with. There are several available. We’re picking the AVA unit test framework https://github.com/avajs/ava for its simplicity.

  • Let’s start writing the testing function in test_sleeps.js
"use strict";

import test from 'ava';
  • That should be enough to have a breaking test. On the console run npm test. This of course fails since we’ve not installed AVA yet:

ava_not_installed

  • npm install --save-dev ava@next
  • Update the package.json file to run the AVA tests: `”test”: “ava”
  • npm test now complains, correctly, that it can’t find any test files
  • Add a section to package.json to tell AVA where to find the tests:
  "ava": {
    "files": ["test_*.js"]
  }
  • Now AVA runs fine but complains it has no tests.
  • Add some simple test code. Remember we’re testing whether the correct parameters have been passed to the function. These parameters will be in the Azure Functions request object:
import sleeps from "./sleeps.js";

test("Request has required parameters", 
    function (t) {
        const req = { "query" : {}};
        t.false(sleeps.has_required_params(req));
    });
  • This now fails since we haven’t written the sleeps.js code yet. Let’s start with the simplest thing that changes the test result:
"use strict";

function has_required_params(req) {
    return false;
}

module.exports.has_required_params = has_required_params;
  • Now for the first time we have a passing test!
  • What remains is to iteratively add test cases until we have test coverage:
test("Request has required parameters", 
    function (t) {
        const req = { "query" : {}};
        t.false(sleeps.has_required_params(req));

        req.query = {"year": "1234"};
        t.false(sleeps.has_required_params(req));

        req.query = {
            "year": "1234",
            "month" : "56",
            "day": "78"};
        t.true(sleeps.has_required_params(req));        

    });

…and the associated implementation in sleeps.js

function has_required_params(req) {
    return Boolean(req.query.year) 
        && Boolean(req.query.month)
        && Boolean(req.query.day);
}

module.exports.has_required_params = has_required_params;
  • The last step is to plug that back into the Azure Functions code:
import sleeps from "./sleeps.js";

...

    if (sleeps.has_required_params(req)) {
        const target = moment({year: req.query.year,
            month: (parseInt(req.query.month) - 1), // JS Dates are zero indexed!!!
            day: req.query.day });

...

…and git push to deploy up to the Azure Portal and test.

deploy_sleeps

  • Except that didn’t work because Azure Functions don’t support ES6 imports yet:

import_error

  • Modify the code to use the node require() form of module import. Of course changing the test code first and then the Azure Function.
const sleeps = require("./sleeps.js");

Conclusion

We’ve created a unit testing pipeline for Azure functions allowing us to develop in a test-first style. In the process we have factored application logic out of the Azure Functions code nicely separating concerns of application logic vs. handling web requests.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s