Should You Jump into Unit Testing before OOP?
Sometimes it’s hard to choose which programming fundamental you should start focusing on next. You might feel pressure to gain a firm grasp on OOP principles before moving on to design patterns or unit testing.
Say, however, you have a loose understanding of OOP principles and you want to work on your biggest deficit, which is unit testing. Is it OK to go against the logical progression of learning more advanced OOP topics before unit testing?
While taking any direction is always better than no direction - getting better with unit testing before getting better with OOP is a perfectly acceptable learning path.
Refactoring
If you plan to look into unit testing and you’re not confident with OOP, you should try to strengthen your refactoring skills first.
Refactoring “…is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior.” - Martin Fowler, 1999
Below is a method from a project that I inherited… yes, you read correctly - METHOD. The screenshot is just to get an idea of the size of this method - roughly 800 lines total.
Long Method is one of the first Code Smells described in Martin Fowler’s Refactoring. I used to dread having to debug/change this application. This was not the only long method in the app either, this was the average method size for the app.
I don’t need to talk about why an 800-line method is harder to debug than a 20-line method. There is usually a simple fix for this code smell - the “Extract Method” refactoring.
But what about the process of performing this refactoring? Assuming you have some time to improve this application, what should you do first to set yourself up for success?
Build a Safety Net
Unit testing is your safety net for refactoring. It’s much more effective to write tests for smelly code before making deliberate refactorings.
Identify what the possible inputs are and what the expected outputs are for your to-be-refactored code…
public static DivisionDataHolder LoadData(SqlConnection connection, DateTime StartDate, DateTime EndDate, TreeNode node, int mode, int view, List<GoalHolder> goals, bool includeQuotes, double PacePercentage) {
...1000 lines of code...
return SomeDivisionDataHolder;
}
*“Wait, there are 9 parameters for this 1000-line method!?!?!”* Yep, another code smell… another refactoring (see Replace Parameter with Method, Preserve Whole Object, Introduce Parameter Object)
As you can see, unit testing can compliment and, sometimes, drive refactoring. Viewing it under this “refactoring lens,” unit testing doesn’t have to be coupled with using advanced OOP design techniques.
Here’s a simple entry point to testing my long method…
namespace Project.Tests {
[Trait("These tests are for LongMethod so I can refactor without breaking","")]
public class LongMethodTests {
[Fact(DisplayName = "When I feed the LongMethod some parameters, I should get a valid DivisionDataHolder...")]
public void LongMethodStillWorks() {
var objectWithLongMethod = new ObjectWithLongMethod();
var ParamObject params = new ParamObject() {
Param1 = "param",
Param2 = "param",
Param3 = "param"
};
var expected = new DivisionData() {
data = new bunchOfData();
};
var result = objectWithLongMethod.LongMethod(params);
Assert.Equal(result, exptected);
}
}
}
Once we are in a place where tests can be written to ensure the safety (“safety net”… get it…) of your method, i.e. changing the code doesn’t break the unit tests, we can start some more deliberate, fast, and comfortable refactorings.
Related Posts: