I’m working with a few teams evolving a large complex legacy embedded C application. (Whoa! That is a lot of modifiers on application.) We are trying to get unit tests in place. I think there is some 20 year old code here. And this application is not going away anytime soon. So adding tests is critical to keeping the application running and making it more maintainable for the years to come. The biggest challenge (so far) is getting the setup and initialization code together to allow a unit test to run. The first test is a bear. Once we get one going its much easier to get others going in the same area.
The pain of getting the first test’s data setup is making me long for constructors.
The system is not atypical for a C program. There is a function and data structure free-for-all going on. The data structures are interlinked and accessible though globals. There is a fairly complex initialization that must occur before each test case. It’s a significant investment in test setup code. To make matters worse there are multiple teams adding features to the code base. Nicely they are writing new test cases too, and some sharing of test code.
The first form of this sharing is the infamous cut/paste/modify form of code reuse. One engineer would work through the initialization. Get their tests working, check in the tests. Some colleague finds the test file. Thinks, that is almost meets their needs, copies the file and forces it onto submission. There is almost complete overlap in the setup code. The good news is that our test code base is growing and we are getting a lot of good tests put together. The bad news is that in the process, we are building up a pretty big refactoring debt.
Maybe worse than that, other engineers reinvent the same code. That’s a lot of duplicate effort and code. Why is it only worse, maybe? Because in the long run there is more duplication. The effort to create the test setup code happens once and the effort to deal with the duplicate setup code will happen over and over again for the life of the tests. A change to one of many data structures could break all these slightly different pieces of initialization code. The effort to keep the tests current and the production code running will grow and grow. The tests will be tossed when the going gets tough.
I’ll show you in another post what we’re doing about it, but the whole activity makes me miss constructors and dependency injection. I’d love to just be able to mention a data structure in the code and get one that has been initialized at least to a safe state, if not a useful state. The constructor’s parameter list would show me what is needed to make a useful copy of the object, oops I mean data structure. Right now we rely on hunting through code to figure out what is needed, and if we’re luck we get segmentation faults whenever a null or bad pointer is encountered.
I want encapsulation and construction.