Characterization, an easier way to write tests
Testing is hard. Testing someone else’s code is even harder. But characterization tests make it easier!
We all know testing our code is important, and sometimes we even do it. But when you have to interact with code someone else has written in the past, but not documented, you’re much more likely to hope it does its job, or rewrite it if it doesn’t seem to.
However, there is a type of test made for this purpose — characterization tests! Unlike traditional testing, where you test the code against a known specification, characterization tests assume the code is correct, and test your changes against old behavior. That is, they simply detect changes and warn you about them
Example
Suppose you stumble upon this function (in a groceries list app, for example):
This is sadly very poorly written code, and it isn’t easy to guess what the function is doing just by looking at it. Ideally, we would like to refactor it, but without testing in place we might break the functionality without even knowing it.
This is where we can use characterization tests. Luckily, we found correctly formatted inputs the function is supposed to convert:
We can simply run the function on them, store the outputs and create tests to give us confidence in our changes.
Choose a test framework
Just about any test framework will do the job, so make sure to use your favorite. Since the code is written in Javascript, I chose to use jest, but mocha or Jasmine are great too.
Sidenote: You can even use wish (not Shopping Made Fun), which is probably the easiest to set up and even includes an option for characterization tests.
Get the outputs
Running the code once allows us to map our inputs to outputs:
[["apple", 700, "g"], ["pear", "750", "g"]]: {"apple": 700, "pear": 750}
[["apple", "1", "pound"], ["pear", 2, "kg"]]: {"apple": 454, "pear": 2000}
[["apple", "1", "kg"], ["orange", 2200, "g"], ["apple", 1, "pound"], ["pear", "780", "g"]]: {"apple": 1454, "orange": 2200, "pear": 780}
Then we can write our tests:
Refactoring
Now that we have established our test suite, we can finally refactor without breaking the behavior! Every time we make a change, the passing tests will give us confidence in what we are doing.
For example, here’s how I refactored the function to make it clearer and more modular:
Thanks!
Still reading? You must be pretty good at paying attention!
I would love to hear your feedback on my other projects, so feel free to look at my GitHub ;)
Thanks for reading and happy coding!