What is the Humble Object pattern and when is it useful?
Humble Object pattern is mentioned in the book - Clean Architecture: A Craftsman's Guide to Software Structure and Design by Uncle Bob, Chapter 23 - PRESENTERS AND HUMBLE OBJECTS (https://learning.oreilly.com/library/view/clean-architecture-a/9780134494272/ch23.xhtml).
This pattern is applied at the boundaries of the system, where things are often difficult to test, in order to make them more testable. We accomplish the pattern by reducing the logic close to the boundary, making the code close to the boundary so humble that it doesn't need to be tested. The exctacted logic is moved into another class, decoupled from the boundary which makes it testable.
- Robert C. Martin
This pattern is useful when working with UI related code or database code. The main idea is to make the layer which changes frequently like UI code or database code as thin as possible. You will not have any unit test for this layer.
Move all the business logic to a different layer and have detailed unit testing for that.
The whole idea is to make the complex or frequently changing layer as HUMBLE or thin as possible and exclude writing unit testing for that (since it will not be worth the effort you are putting into it). Concentrate on the business logic layer by having detailed testing on it.
I'd usually implement this kind of this as an Interface
- then you can use a mocking framework to stub it for testing, and an IoC framework to inject the correct implementation at runtime.
Here's an example from my current project:
public interface IUserInterface
{
string AskUserWhereToSaveFile(
string title,
FileType defaultFileType,
string defaultFileName = null,
params FileType[] otherOptions
);
string AskUserToSelectFileToLoad(
string title,
FileType defaultFileType,
params FileType[] fileTypes
);
void ShowError(string title, string details);
bool AskUserIfTheyWantToRetryAfter(string errorMessage);
}
My Controller then has a dependancy on IUserInterface rather than a concrete view, which allow me to replace user interactions with a stub for testing.
There is a thorough description at xunitpatterns.com.
Basically, you pull all the logic into a separate object which you can easily test - and your "Humble Object" becomes a wrapper around your testable object; it's just that the humble object also has dependencies on things that are difficult to test, like async services or GUI classes. The idea being to leave so little actual logic in the humble object that you don't need to test it, and so don't need to deal with testing the difficult to test dependency.