Dumber Views, Smarter ViewModels (aka the Humble Object Pattern)

Hard-to-test views? Well then, let’s make ‘em dumber! (Or should I say humbler?)

The smart view

I encountered many times code that looks like this (and I myself am guilty of having written similar code):

Why is this ViewHolder so smart?
Why does it know what a product is made of?
Why does it know how to apply a promotion?
Why does it know how to format a price?
Why does it know which prices to show?
Why does it know how to present a product?

This is business logic that should be tested and that doesn’t belong to the view layer.

“But I can test through the UI! I’ll just use Espresso!”

Yes, you can. But UI tests are painfully slow and not so trivial to write. We don’t want to fire up the whole UI just to test if the promotion is correctly applied or the price is formatted how we expect.
Testing this way is harder than it needs to be and as testing becomes harder we’ll try to find excuses to avoid doing it.

“Well, I’ll unit test through Robolectric then”

It’s an improvement. Robolectric tests are less slow and less hard than full-blown UI tests but are still slower and harder than they could.
Why would you choose them over pure view-agnostic unit tests?

Dumb it down

Luckily, the cure is simple and consists in just moving all the logic outside of the view.
So, let’s make our ViewModel smarter:

And our ViewHolder dumber:

Now our unit tests can finally run free from the tangles of the view:

On the other hand, the view logic became so trivial that we can choose to skip testing it or to just write a simple test to verify that the bindings occur correctly.

And that’s all there’s to it!

Note that even though our example revolved around pulling code out the view, the same reasoning holds for any component that is hard to test. Nothing less and nothing more than the Humble Object Pattern .