Thursday, July 28, 2016

Revisited Kata: Using Midje's defrecord-openly to mock a protocol in Ohce

Several weeks ago I did the Ohce kata in Clojure using outside-in TDD with Midje (as I explained in a previous post).

In that post I said that I hadn't used Midje's defrecord-openly and provided macros and had instead decided to write my own code to capture the arguments with which the functions of the Notifier protocol implementation were being called because:
... I didn't like having to use defrecord-openly in production code.
Well, I was wrong!

It turns out that it is not necessary at all to use defrecord-openly in your production code in order to mock a protocol.

I understood it thanks to this answer in Stak Overflow: Mocking Clojure protocols.

The only thing I needed to do was to use defrecord-openly to create a fake implementation of the Notifier protocol inside the test code like this:

and then write the tests against this unimplemented fake.

This are the new Ohce tests:

As you can see it's very easy to use Midje's defrecord-openly to mock protocols.

I just misunderstood Midje's documentation the first time I tried to do it...

Friday, July 22, 2016

Interesting Paper: "Why should physicists study history?"

I've just read this wonderful paper by Matthew Stanley This is a gem full of wonderful ideas about diversity, group dynamics, critical thinking and the pursue of knowledge that I think are also valuable to software development and, probably, to any team or community effort.

Tuesday, July 19, 2016

Revisited Kata: Listening to the tests to improve the design of Ohce

A couple of weeks ago, I did the the Ohce kata in a Barcelona Software Craftsmanship event (I wrote a previous post about it).

I wasn't happy with the resulting tests for the Ohce class:

What I didn't like about the tests was that they weren't independent from each other.

When I started doing TDD, they were independent, but as soon as I introduced the loop to request more user phrases, I had to stub a call to the PhraseReader in each test that returned the stop phrase to avoid infinite loops.

This made the tests to be very coupled to Ohce's implementation:

Another problem was a violation of the Law of Demeter caused by asking the Phrase value object, being returned by the PhraseReader, if it was a palindrome.

This made it necessary to stub some calls to the PhraseReader returning dummy phrases to avoid getting null pointer exceptions.

These problems in the tests were a signal of problems in the design (see What the tests will tell you).

I think there were two main problems:
  • Ohce code had too many responsibilities.
    • Controlling the dialog with the user.
    • Responding to the user inputs.
  • Ohce code was too procedural.

How could I fix them?

Well, I started thinking about a metaphor in which Ohce was having a dialog with the user and responded to each user phrase, and how this dialog might be infinite unless the user made it stop by introducing an stop phrase.

This made me realize that there were some missing abstractions.

So I decided to explore this metaphor of an infinite dialog comprised of responses to each user phrase:

InfiniteDialog only had the responsibility of going on reading and responding to the user phrases until told to stop.

Going on with the metaphor, each of those responses to the user input might be seen a sequence of two responses: one that reversed the user phrase and another one that celebrated when the user phrase was a palindrome.

To compose several independent responses, I used a decorator:

Then I just needed to separately code each concrete response:

Notice how all the tests in InfiniteDialogTest, SequenceOfResponsesTest, ReversingResponseTest and PalindromeResponseTest were just adaptations of tests that were originally in OhceTest, only that now they were much more focused and simple.

I regard this as a sign of design improvement.

Finally, I refactored the code to make Ohce start using the InfiniteDialog by applying parallel change. It was a very smooth refactoring during which no tests were broken.

I think that this smoothness was a special case, because all the expectations in old Ohce's tests were also satisfied using InfiniteDialog. Using InfiniteDialog just added some new layers of abstraction over old Ohce's collaborators, but behind those layers the old collaborations were still happening.

In a different case, I might have probably broken some Ohce's unit tests, but that wouldn't have been a problem because the behavior would have been still protected by the end-to-end tests. So, I could have just deleted the broken unit tests and written a new one for the collaboration between Ohce and Dialog.

These were the new tests for Ohce:

And this is its new code:

We've seen how listening to some problems in Ohce's tests, led me to find some design problems, that once fixed produced much more focused, clearer and less coupled tests, and how having used mocks didn't suppose any problem at all during the whole refactoring process.

--------------------

You can find all the code in this GitHub repository.