This time I want to take a look at some of the common patterns we can use to extract parts of our code into dependency-lite or dependency-less classes that we can unit test without needing mocks.
I’ll have to admit, I haven’t really be practicing this approach for a long time, so my list of patterns is probably lacking, but these are some of the ones I kept seeing as I was working through the code.
If you have some ideas for other patterns or know of some already or have better names than what I have chosen, please let me know.
Pattern 1: Pull out state
In many cases we can have complex objects that contain their own state often split across member variables.
If you find that a class has many methods that check some member variable and do one thing or another based on what value that member variable is set to, you might be able to benefit by extracting all the state changing logic into a separate class and creating events to notify the original class when the state changes.
We can then keep the new class as the single source of state for the class it came from and easily write dependency free (level 2) unit tests for the state class.
From the perspective of the class you are pulling the state out of, we might turn:
Obviously in this case there would be many ways the states get changed. I haven’t included those here, but you can imagine how those would become part of the new state class.
Pattern 2: Method to class
Often I have found that I can’t find a way to extract all of the logic out of a class into one cohesive class, because of multiple interactions inside the class.
When I encounter this problem, I have found that I can usually identify a single large method that is using data from dependencies in the class to perform its logic.
These methods can usually be extracted to be their own new class. We can pass in the data from the dependencies the method was originally using instead of using the dependencies directly.
Good candidates for this kind of pattern are methods which use multiple dependencies to get data in order to compute a single result.
When applying this pattern we may end up with a very small class, containing only 1 or 2 methods, but we are able to easily unit test this class with state and dependency free unit tests (level 1.)
Pattern 3: Pull data sources up
It is often the case that we have dependencies in a class that exist only to provide some sort of data to the actual logic in the class.
In these situations it is often possible to pull the source of the data up and out of the methods that use the data and instead pass in just the used data.
Performing this refactoring allows us to have methods in our class that do not use the dependency in the class directly, but instead have the data that dependency class provided passed in.
When we do this, it opens up the ability to write clean and simple unit tests without mocks for those methods in the class or to apply the “Method to class” pattern to extract the method into its own testable class.
As a first step to refactoring my code to make it easily unit testable, I will often scan the methods in the class to find methods that are only using data from dependencies, but not manipulating the state or sending commands to the dependencies. As I am able to identify these methods, I can apply this pattern to remove the dependencies from the methods.
Pattern 4: Gather then use
Often we find that we cannot pull a dependency out of a method because that dependency is being manipulated inside of a loop or in the middle of some process.
In cases like these, we can often gather all the data that will be used to manipulate the dependency in some form of a collection and then reiterate through that collection to perform the manipulation on the dependency.
A good way to envision this is to think about two people mailing letters. In one scenario we might have the first guy stuffing the envelope and the 2nd guy sealing the envelope.
We could carry on this process of 1st guy stuffs, 2nd guy seals for each letter we want to mail, but the process is dependent on both guys being present and working.
If we change it so that the first guy stuffs all the envelopes first and gives that stack of stuffed envelopes to the 2nd guy, they can do their jobs independently.
By applying the same idea to algorithms in our code, we can take a single method and break off the parts that don’t rely on dependencies to test independently or even move to their own classes.