Sunday, April 27, 2014

TDD and Rails - what makes a good Unit?

There is an ongoing discussion about TDD and Rails. It was recently heated by some some of the DHH statements in his RailsConf keynote and in the blog post:

One aspect of this discussion is the confusion about - What makes a good Unit?

In the Rails community, I've seen people overusing mocks and stubs - you can detect it by looking at all the "should_receive" calls in the codebase. They're not always bad, but they might be potential code smells.

The reason we use should_receive is in a way to draw the boundary between what's important to test right now and what's outside of the test scope.

Unit example

Let's take an example - you've got an Order, which can have many OrderLines, a ShippingAddress and a Customer.

Do we have 4 units here, representing each class? It depends, but most likely it may be easier to treat the whole thing as a Unit. You can write a test which test the whole thing through the Order object. The test is never aware of the existence of the ShippingAddress. It's an internal implementation detail of the Order unit.

A class doesn't usually make a good Unit, it's usually a collection of classes that is interesting.

This way of defining a Unit gives you tests, that don't need to change whenever the internals change - that's a good thing. You don't want to change tests on every refactoring - in fact it's a smell.

Unit-based architectures

Some time ago, I wrote a surprisingly popular post: The four architectures that will inspire your programming in which I listed:

  • Hexagonal Architecture
  • Clean Architecture
  • DDD
  • DCI

In a way, all of them are focused on defining what a good Unit is and how to separate it from other Units. 

DDD has the concept of an aggregate, quote: "A DDD aggregate is a cluster of domain objects that can be treated as a single unit."

Clean Architecture has the concept of use-cases which touch the topic slightly differently (by operations, not units), but overall it's very similar:

Hexagonal Architecture is all about a Unit surrounded by adapters, in my interpretation. They often call it the Middle Hex.

Last, but not least - DCI. This architecture deserves a special mention here. DHH quoted James Coplien in his TDD talk. James has been famous not only from his strong opinions on TDD, but more from his activity in the DCI world. He's one of the fathers of this movement. DCI is the most inspiring architecture here. Ruby and DCI makes a fantastic combination, however not all can work as in the DCI theory. DCI gives good tools for defining what a Unit can be. In short, their approach to what a Context is, may be used to defining Units. A Unit is this paradigm is a collaboration of objects. Read more here:

Have fun in researching more!

If you want to follow more of my work - I'm writing a book on Refactoring Rails apps, which is already available. At the moment, I'm writing new chapters on how to write tests that support refactoring of Rails apps.

No comments: