Unit testing is incredibly important. I learned that early in my career. And here is my story.
When I studied Computer Science at Northeastern a million years ago, I was never taught to write tests for my code. So, when I got my first real job, the idea of a unit test was a foreign concept. One of the first code bases that I worked on professionally was a financial calculation library. A fixed income analytics library and service. Specifically, a java wrapper around a C library that was converted from Fortran.
Don’t judge me. I inherited it.
At first, my team’s responsibility was the java service and library. The C library was maintained by another engineer. The java code was ugly and frustrating to work with and the service was SLOW. The main purpose of the library was to take inputs to the library and route them to proper calculation after validating and converting the parameters for the C code. The service provided caching and external data enrichment for daily indexes that were also passed in as part of the calculation.
Please don’t ask me about the math. Fixed income analytics was also not something I learned as a computer science student.
Our first big project was to improve performance and reduce cost by porting the code from an enterprise unix machines to a cheaper Linux platform. While doing so, we needed to prove that we were not introducing risk to trading platforms that used the results of these calculations for large volume trading. How do you prove that a performance refactoring effort and a port to a new operating system does not impact a calculator?
I bet you saw that coming.
The code base had no tests at all. Before starting our refactoring effort, we wrote a test harness around the calculation library. We mocked inputs for every calculation and captured the result as a baseline. One of the most complicated parts of the library was the need for date math and holiday calendar logic. Our tests needed to use dates that landed on banking holidays, leap years, ends of the month etc. The tests also needed to cover bad inputs to see how the application responded. We used those results to validate any changes we made to the code thereafter.
You know what I bet you didn’t see coming?
How much we all enjoyed it!
While I knew nothing about fixed income math before the project, I knew more than I ever wanted to know after the project. Writing tests required us to understand the different types of calculations that our library supported. We started to feel ownership of the code base because we knew what was required to make it work. In order to test a calculation that required a yield curve, we needed to understand how a proper yield curve was constructed. In order to understand yield and pricing calculations, we needed to learn about bond coupons and how they were structured in life and in code. Seriously, we learned a lot.
We could tune performance
With a unit test harness, we now had calculations that we could wrap in performance tests and loop in 100 or 1000+ iterations with a profiler attached. Shockingly, we found that our library spent over 50% of its time doing date math and string manipulation and string concatenation in logging statements! We focused efforts to improve the code and were able to use our unit tests to verify that we had not broken anything.
We could give a high degree of confidence to people who cared with minimal effort
With hundreds of tests that could be run against the code in the new environment and with our performance improvements, we had statistics to show that we had accomplished our task. Did we have to write hundreds of tests in code?
We developed a unit testing framework that let us write extensible tests for these calculations. Simple files that had the inputs and expected outputs for calculation types made testing the carious scenarios scalable and nearly effortless. Sometimes, being creative with how you test is just as important as being creative with how you write production code.
But why does this matter in the long run?
With the diligence and success of that project, we had set ourselves up for the future. Later, the C developer left and we were responsible for calculations that required an advanced quant degree. With our tests, we could make changes to lower level code and make sure that we broke what we expected and fixed/improved what was we intended. We were also able to take on large refactoring efforts to make the code readable and maintainable and therefore, our lives more enjoyable.
In the end, units tests are about making you confident and proud about the code you write.