The Humble Object Pattern is a design pattern to make especially unit testing easier with the goal of separating behaviors that are easy to handle (domain logic) from behaviors that are hard to handle (like external events or dependencies).
So let's have a look at what it is and how you can utilize it.
The Humble object
When we write programs, they often need to communicate with other parts of the computer or the outside world, like a database or internet service. But when we want to test our programs to make sure they work correctly, it can be hard to talk to these other parts.
To solve this problem, we can use the Humble Object Pattern to separate the parts of our program that need to talk to other parts of the computer or the outside world from the parts of the program that don't. The parts of the program that don't need to talk to the outside world are much easier to test because we can test them in isolation without worrying about other things.
In easy steps: Take out the hard - testing parts and put it into a wrapper that you can stub/mock in your test. That wrapper is called Humble object. You ever had a dependency to DateTime.UtcNow
in your code? If so, it can be really tricky to have stable tests depending on what you do with "now". So we can just "extract" that part and put it into a wrapper:
public interface IDateTimeProvider
{
DateTime UtcNow { get; }
}
public class DateTimeProvider : IDateTimeProvider
{
public DateTime UtcNow => DateTime.UtcNow;
}
public class MyService
{
public MyService(IDateTimeProvider dateTimeProvider) { ... }
}
The good part is that we have encapsulated hard-to-test stuff and easy-to-test stuff. And now we are going full circle: The hard-to-test stuff, ideally, doesn't need a test anymore anyway. Testing DateTime.UtcNow
isn't a great idea in the first place. First, how would you setup a test in the first place and second, it isn't your code. DateTime.UtcNow
is 3rd party code from your point of view - and don't unit test third-party code.
Conclusion
The Huble object pattern can make your life easier - especially at borders of your architecture. We saw it with the example of the system cloak, but you can also apply this rule for things like I/O access or something over the network.