Mech Retriever Dev Blog
I’ve been wrestling with the task of cleaning up some of my tests as I work through the Battle Screen, and came to an interesting resolution.
As always, I’ve been alternating between adding features–expanding–and cleaning up the code–contracting. My process “breathes” much like everything else in the universe. Much of the time, moving repeated code to a base class is useful, but only to a point–you want to keep hierarchies flat, not having a massive chain of inheritance. But then you don’t want a massive, “god” base class either, which is what my test code for the BattleScreenDriver was starting to look like.
I tried moving code to external classes that would contain the test helper functions, but that started looking ugly and I realized I was on the wrong track when ultimately it smelled like “feature envy”–where a class is mostly just modifying the data on an existing class. Feature envy usually means the behavior should just be on that class, but then I would be back to square one.
So I looked at it from the perspective of “class cohesion.” Were there clear subsets of methods that cared about subsets of data? Bingo! But how to deal with it?
I tried making extension methods–making test extensions for the objects that I was reasoning about in my test base class. But that felt wrong and weird. That’s when I stumbled on the recommendation that most of the time, if you want to use an extension method, you should just make a class that inherits from the type you are extending and adds the features you want.
Ah-ha! This, indeed, solved a whole category of problems. Now, I could make “TestPlayer,” “TestMapMatrix,” etc. These objects would not be Fakes, because they are not “stand-ins” for the base types. Instead, they are instances of those types, just with some additional test logic.
This cleaned up the test code quite a bit, and now I have a powerful tool for better test design.
Test, Refactor, Win!
May your giant robot crush all that oppose it.