Your "First" Test

If you have been following along you've already written your first test on this mess of a project, but if you've been following along you also probably don't know you to write "your" first test. You wrote mine.

So before we go and fix a defect let's look at the options for writing the first test in a system. Fortunately you have options for choosing your first test in an existing system.

Do you have tests?

Most code doesn't have automated tests, the kind that the developer can run at their laptop. Even tested code often has huge gaps especially if the team has adopted TDD on an existing codebase. If your code doesn't have any unit tests you need to start figuring out what the code does. Later in this book we'll cover Characterization tests, and if you don't have tests you should start there.

Do you have a test framework?

If code doesn't have any tests it almost certainly doesn't have a test framework. If you're in a situation where your co-workers are skeptical of this "Agile Bullshit" then spending a day or two trying to get a test framework to work on their codebase isn't likely to help. Indeed at some organizations you'll need approval to add a third party library, and whomever is in charge of such things is likely to say no.

You have an ethical obligation to do the best you can, and that means writing tests. If there's no test framework and TDD isn't an accepted practice at your company then you need to be writing tests with assert. Check out the chapter on Dirt Simple Tests to see how you can write tests even when your coworkers are hostile to them.

The System has tests - but they suck

This is the most the most likely state of your system. The tests could be confusing, slow, too high level, or written in another programming language. I've seen a lot of terrible "unit" tests and they run the gamut from tests that assert nothing to tests that use the GUI to automate external applications to read files. They can actively slow down the development team, or they get ignored. In many cases you'd be better off deleting them.

However considering this is your first test you're better off using the tests already exist as a starting point. The tests we used in the previous chapter use http requests to validate the behavior of TODO list app. This is a terrible idea. HTTP requests are a coincidence created because we want this application to run in a web browser. They have little to with your application.

Yet when you write your next test, you wrote am http reqest. Why? Because when you did this you were able to learn about your app as it is today, not how it will be in some ideal world. Real World applications are filled with failed architectures, refactorings that didn't work, crude code written by beginners, and mistakes. In Real World TDD we start with what we actually have not some utopian ideal of the perfect app.

Does that mean we don't strive for great code? Of course not. The code you write today should be better than the code you wrote yesterday, and worse than what you'll write tomorrow. Each commit should clean up a little bit of the code from before, reflecting your growth as a developer. What it does mean is you shouldn't let the perfect be the enemy of the good. If you have tests, use those to create a new test to move forward.

And after you get that test passing, refactor. Let's return to our test and see how we might do that.

The Test

it ("does stuff", function(doneringdong) {
    http.get("http://localhost:8888/thingthatwontexist", function(res) {
        expect(res.statusCode).to.equal(404);
        doneringdong();
    });
});

This test has a few problems. It's over HTTP, it has a terrible description, and it fails. Let's start by fixing up the description:

it ("returns a 404 for non-existent paths", function(doneringdong) {
    http.get("http://localhost:8888/thingthatwontexist", function(res) {
        expect(res.statusCode).to.equal(404);
        doneringdong();
    });
});

Now let's make the dumbest change we can make to get the test to pass, so that we can do some refactoring on green.

module.exports = {
  start: function(port) {
    server = http.createServer(function(request, response) {
      if (request.url === "/thingthatwontexist") {
        response.writeHead(404);
        response.end();
      } else if (request.url === "/") {

That right there is some terrible code. Typically we'd either remove the duplication of the string "/thingthatwontexist" or write a second test that forces us to remove the duplication. We're not gonna do that yet. Instead we'll do some safe refactorings.

1. https://en.wikipedia.org/wiki/Characterization_test