We all know that there are different design patterns. They are all quite trivial to learn, but, the trick lies in applying them. When should we use this or that pattern and will that help in making our code better and cleaner. Well, tests are code as well and, you guessed it, there are some testing patterns that are around for a while.
Today, we will take a look at one of them. It is a useful one and also it has an interesting name - the Humble Object Pattern.
Think about this. You are building an API client/wrapper. You read the documentation, you understand the model and the intent of the API and how everything is composed. You start writing your code, keeping it in small chunks (i.e. classes). You are a responsible programmer, you want to have your code well documented and tested. All of this is nice. And then, you get to this part where you start mocking your HTTP calls and stuff easily gets messy.
How can we avoid this? Is there a better way?
Let us take a detour. A bit of history first. I think that Uncle Bob has a really good definition of the Humble Object pattern in Episode 23 of his Clean Code videos:
In case you find the definition confusing, let’s tear it apart. First, the boundaries. When one says boundaries, it means that the person is referring to the part of the system that communicates with other software that is not written by you, but your software is dependent on it. For example, let’s say your software creates cron jobs. The boundary lies beween the software that will call the cronjob system command and the operating system.
So, this means when using the Humble Object pattern, we extract as much logic as we can from the boundary class(es), thus making them humble. The extracted logic will be moved to another class, which will be easily testable. On the other hand, the humble code will be dependant on the extracted class, but testing it won’t be necessary, because it doesn’t hold any business logic.
Usually, when explaining the humble object, people use GUIs or async code as examples. We won’t go down that path today. Let’s try finding an example which we might run into more frequently.
Back to the problem
Now, that we undestand the motivation and the theory behind this pattern, let’s continue with aforementioned design problem.
Tackling any programming problem, in my opinion, is best understood via some code. Here’s an example. We will make a tiny API wrapper of the REST Countries API, more specifically, the World Capitals API.
CapitalsClient will issue a GET request to the API endpoint, get the
result and build a
Country object from the results. This is what the JSON
result looks like for London, UK:
For completeness sake, let’s see the
Now, let’s revisit the
CountryClient class. We’ve all done this - we fetch
the JSON, parse it and build the currencies and country object(s). Now, testing
First, we’ll need to stub the GET request using
Webmock and assert on the
object that we receive as a result of the
Another approach is to use VCR and record the request going out and replay it
Now, this works, it’s fine. But, what exactly are we testing here? I am sure we
have all done this multiple times. Look at the test class name -
CountryClientTest. We should be testing the client, not setting assertions
on the country object. The
CountryClient acts like a factory, not like an
Also, think about this - stubbing, although it looks fine, it’s basically isolation. While we cannot completely rely on pulling real data from the API for each of our tests, we shouldn’t also go overboard with it.
While one can argue that stubbing external services is all good, what would the case be if you used a library that was actually fetching the data and building it for you? What would you test if the wrapper was made by someone else and you are using it in a Rails app? You could go on and stub the library, but you have no idea if the API and/or the wrapper had any changes made to them.
But, let’s take a step back. How can we apply the humble object pattern here?
Applying the pattern
If you remember, the pattern states that we need to extract most of the logic near the boundaries of the system, so the code on the boundary itself is so humble, it doesn’t need to be tested. But, the humble object should be dependent on the extracted code.
Let’s try to refactor our code by doing exactly that.
CountryClient. It sure does more than it should. Let’s make it
humble. The first step would be to make it an API client. Exactly that,
nothing more or less. This means that it will only issue HTTP GET to the API.
Hint: think of the Single-responsibility principle.
That’s it. Again,
CapitalsClient just sends the request and it returns it’s
response. Now, the extracted logic.
Since the code that we extracted was actually building the
Country object, we
can create a
CountryBuilder class out of it:
CountryBuilder.build method will receive the JSON and build the
object on it’s own. The next, obvious step, is to test this class. If you look
at the test we wrote earlier, you can notice that the test is 90% done, it just
needs some tweaking.
The key differences in the new and old test is that the new one is missing the
Webmock request stub. Also, we are testing the builder class, which does what
it should - receives the response as a JSON, builds an object of a class and
returns it. But, what happens now to the
CapitalsClient? Well, nothing too
complicated. If you remember, the pattern states that the humble object should
depend on the extracted code.
If you look at the code below, it should all make sense:
So, we add the dependency, making
create the country out of the JSON payload.
And, what about testing
CapitalsClient? Well, we don’t really need to test
it. Even if we wanted to test it, we could only write a test with a test spy
that would expect
CountryBuilder.build to be called. But, how useful is that
test? If we wrote it, we would tie our test to the implementation of the
production code. This means that if the implementation of this method changes
in the future, but it’s output does not, our tests will fail although we
haven’t broken our code.
As you can see, although very simple, this humble pattern can make a difference when we want to leave out heavy stubbing to external services, APIs or interfaces and just focus on our code where “the magic” happens. Also, at least for me, this type of refactor comes very natural in these situations. But, knowing the pattern guides us towards making only one entry point to our code (or, dependency).