Monday, November 24, 2014

Thoughts On Unit Testing

Adopting unit testing ...

Much has been said and written about unit testing before. Those previous comments convinced me to try to add unit tests to whatever code I was writing. As I have gone from a non-unit tester to a novice/intermediate one, I have observed a few things.

Unit testing instead of print statements.

Every programmer begins by writing a "Hello World" program. By writing such a program, you learn how to compile, run your task and verify your output. As I built more programs, I still had the old "Hello World" verification process in mind, and I often littered programs with print statements. These print statements would give me confidence that things were working as designed. After I was sure a function or class was working as expected, I would clean up these print statements that I had been using to help me develop.

It dawned on me that print statements that I used during development could be replaced by unit tests. If I wanted to check if a function was being called, I could write an "EXPECT_CALL" test, or if I wanted to validate that a variable was updated in some way, I should invoke an "EXPECT_EQ" test.

Advantages to Unit testing instead of print statements.

There are lots of advantages of using unit tests over print statements. You ask a computer to verify output rather than a human (you) from reading a terminal screen. Verifying mundane things over and over again is ideal work to shift from you to a computer. You keep unit tests in your source control even after you ship your code to production. Which means that your tests can be run again and again for all of time.

The only advantage of using print statements is that it is easy to use. This is true in the absolute bare minimum short run. But for any complex piece of code, it is not true. Are global variables easier to use than classes with encapsulated data? Yes, it is easier to directly modify and access a variable that sits in the global scope. However, it is much harder to understand and debug code that relies on global variables. Over time, the hard-to-use classes that contain private data, setters/getters, and other complexities actually become easier to use. You just have to get used to it. The same goes for unit tests. At first, they seem to add a lot of extraneous code for something that is clearly trivial, but over time, they are worth it.

My way of doing test driven development (TDD)

I have heard that test driven development is the way to go. In practice, I have found it a little bit odd to write a test before doing anything. I mean, how do you write a test for a function that does not exist? Maybe this is clear to others, or maybe I am doing things the wrong way, but here is how I do TDD.

  1. Write an interface (either a class or function). The interface should have some dummy implementation so that it compiles and even runs.
  2. Write one test that checks one feature of the interface. This test should fail.
  3. Update the implemntation such that the one test pases.
  4. Repeat steps 2 and 3, until your implementation supports every feature you need.

I find that this process makes me think about a good interface, and it makes me think whether any feature would pass the old YAGNI (You Aint Gonna Need It) smell test. It works for me, for now.

No comments:

Post a Comment