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:

(unfinished greet)
(unfinished echo)
(unfinished palindromes-rock)
(unfinished bye-user)
(defrecord-openly FakeNotifier []
notifications/Notifier
(greet [this greeting])
(echo [this reversed-phrase])
(palindromes-rock [this])
(bye-user [this name]))
and then write the tests against this unimplemented fake.

This are the new Ohce tests:

(ns ohce.unit-tests.ohce-test
(:require
[midje.sweet :refer :all]
[ohce.ohce :refer :all]
[ohce.notifications :as notifications]
[midje.open-protocols :refer [defrecord-openly]]))
(unfinished select-greeting)
(unfinished read-input)
(unfinished greet)
(unfinished echo)
(unfinished palindromes-rock)
(unfinished bye-user)
(defrecord-openly FakeNotifier []
notifications/Notifier
(greet [this greeting])
(echo [this reversed-phrase])
(palindromes-rock [this])
(bye-user [this name]))
(facts
"about ohce"
(fact
"it greets the user"
(let [notifier (->FakeNotifier)
stop-word "Stop!"]
(ohce select-greeting notifier read-input stop-word ...username...) => irrelevant
(provided
(read-input) => "Stop!"
(select-greeting ...username...) => ...greeting...
(greet notifier ...greeting...) => irrelevant :times 1)))
(fact
"it reverses the user inputs"
(let [notifier (->FakeNotifier)
stop-word "Stop!"]
(ohce select-greeting notifier read-input stop-word ...username...) => irrelevant
(provided
(select-greeting ...username...) => irrelevant
(read-input) =streams=> ["hola" "lolo" "Stop!"]
(echo notifier "aloh") => irrelevant :times 1
(echo notifier "olol") => irrelevant :times 1)))
(fact
"it ignores inputs that are blank"
(let [notifier (->FakeNotifier)
stop-word "Stop!"]
(ohce select-greeting notifier read-input stop-word ...username...) => irrelevant
(provided
(select-greeting ...username...) => irrelevant
(read-input) =streams=> ["memo" "" "moko" "Stop!"]
(echo notifier "omem") => irrelevant :times 1
(echo notifier "okom") => irrelevant :times 1)))
(fact
"it identifies palindromes"
(let [notifier (->FakeNotifier)
stop-word "Stop!"]
(ohce select-greeting notifier read-input stop-word ...username...) => irrelevant
(provided
(select-greeting ...username...) => irrelevant
(read-input) =streams=> ["oto" "ana" "Stop!"]
(echo notifier "oto") => irrelevant :times 1
(echo notifier "ana") => irrelevant :times 1
(palindromes-rock notifier) => irrelevant :times 2)))
(fact
"it knows when to stop"
(let [notifier (->FakeNotifier)
stop-word "Stop!"]
(ohce select-greeting notifier read-input stop-word ...username...) => irrelevant
(provided
(select-greeting ...username...) => irrelevant
(read-input) =streams=> ["Stop!"]
(bye-user notifier ...username...) => irrelevant :times 1))))
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:

package com.dodevjutsu.katas.ohce.tests.unit;
import com.dodevjutsu.katas.ohce.core.*;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Before;
import org.junit.Test;
public class OhceTest {
private Mockery context;
private GreetingsSelector selector;
private Notifier notifier;
private Ohce ohce;
private PhraseReader phraseReader;
private String userName;
private Phrase stopPhrase;
@Before
public void setUp() {
context = new Mockery();
selector = context.mock(GreetingsSelector.class);
notifier = context.mock(Notifier.class);
phraseReader = context.mock(PhraseReader.class);
String stopPhraseContent = "Stop!";
ohce = new Ohce(stopPhraseContent, selector, notifier, phraseReader);
userName = "Juan";
stopPhrase = new Phrase(stopPhraseContent);
}
@Test
public void greets_user() {
String greeting = "¡Buenos días Juan!";
context.checking(new Expectations() {{
allowing(phraseReader);
will(onConsecutiveCalls(
returnValue(new Phrase("not used")),
returnValue(stopPhrase)
));
oneOf(selector).selectGreeting(userName);
will(returnValue(greeting));
oneOf(notifier).greet(greeting);
ignoring(notifier);
}});
ohce.run(userName);
context.assertIsSatisfied();
}
@Test
public void echoes_the_reversed_user_phrase() {
Phrase phrase = new Phrase("hola");
Phrase reversedPhrase = new Phrase("aloh");
context.checking(new Expectations() {{
ignoring(selector);
exactly(2).of(phraseReader).read();
will(onConsecutiveCalls(
returnValue(phrase),
returnValue(stopPhrase)
));
oneOf(notifier).echoReversedPhrase(reversedPhrase);
ignoring(notifier);
}});
ohce.run(userName);
context.assertIsSatisfied();
}
@Test
public void identifies_palindromes() {
Phrase palindrome = new Phrase("ana");
context.checking(new Expectations() {{
ignoring(selector);
exactly(2).of(phraseReader).read();
will(onConsecutiveCalls(
returnValue(palindrome),
returnValue(stopPhrase)
));
oneOf(notifier).echoReversedPhrase(palindrome);
oneOf(notifier).palindromesRock();
ignoring(notifier);
}});
ohce.run(userName);
context.assertIsSatisfied();
}
@Test
public void says_bye_when_told_to_stop() {
context.checking(new Expectations() {{
ignoring(selector);
oneOf(phraseReader).read();
will(returnValue(stopPhrase));
oneOf(notifier).sayBye(userName);
ignoring(notifier);
}});
ohce.run(userName);
context.assertIsSatisfied();
}
@Test
public void keeps_asking_for_the_user_input_until_told_to_stop() {
context.checking(new Expectations() {{
ignoring(selector);
exactly(3).of(phraseReader).read();
will(
onConsecutiveCalls(
returnValue(new Phrase("pepe")),
returnValue(new Phrase("moko")),
returnValue(stopPhrase)
)
);
oneOf(notifier).echoReversedPhrase(new Phrase("epep"));
oneOf(notifier).echoReversedPhrase(new Phrase("okom"));
oneOf(notifier).sayBye(userName);
ignoring(notifier);
}});
ohce.run(userName);
context.assertIsSatisfied();
}
}
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:

package com.dodevjutsu.katas.ohce.core;
public class Ohce {
private Phrase stopPhrase;
private GreetingsSelector selector;
private Notifier notifier;
private PhraseReader phraseReader;
public Ohce(String stopPhraseContent, GreetingsSelector selector, Notifier notifier, PhraseReader phraseReader) {
this.stopPhrase = new Phrase(stopPhraseContent);
this.selector = selector;
this.notifier = notifier;
this.phraseReader = phraseReader;
}
public void run(String userName) {
greet(userName);
processPhrase(phraseReader.read());
sayBye(userName);
}
private void greet(String userName) {
notifier.greet(selector.selectGreeting(userName));
}
private void sayBye(String userName) {
notifier.sayBye(userName);
}
private void processPhrase(Phrase phrase) {
if(shouldStop(phrase)) {
return;
}
notifier.echoReversedPhrase(phrase.reversed());
if(phrase.isPalindrome()) {
notifier.palindromesRock();
}
processPhrase(phraseReader.read());
}
private boolean shouldStop(Phrase phrase) {
return stopPhrase.equals(phrase);
}
}
view raw OldOhce.java hosted with ❤ by GitHub
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:

package com.dodevjutsu.katas.ohce.tests.unit;
import com.dodevjutsu.katas.ohce.adapters.dialogs.InfiniteDialog;
import com.dodevjutsu.katas.ohce.core.Dialog;
import com.dodevjutsu.katas.ohce.core.Phrase;
import com.dodevjutsu.katas.ohce.core.PhraseReader;
import com.dodevjutsu.katas.ohce.core.Response;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Before;
import org.junit.Test;
public class InfiniteDialogTest {
private Mockery context;
private Dialog dialog;
private Response response;
private PhraseReader phraseReader;
private String stopPhraseContent;
@Before
public void setUp() {
context = new Mockery();
phraseReader = context.mock(PhraseReader.class);
response = context.mock(Response.class);
stopPhraseContent = "Stop!";
dialog = new InfiniteDialog(phraseReader, response, stopPhraseContent);
}
@Test
public void keeps_asking_for_input_until_told_to_stop() {
context.checking(new Expectations() {{
exactly(3).of(phraseReader).read();
will(onConsecutiveCalls(
returnValue(new Phrase("pepe")),
returnValue(new Phrase("moko")),
returnValue(new Phrase(stopPhraseContent))
));
oneOf(response).respondTo(new Phrase("pepe"));
oneOf(response).respondTo(new Phrase("moko"));
}});
dialog.start();
context.assertIsSatisfied();
}
}
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:

package com.dodevjutsu.katas.ohce.tests.unit;
import com.dodevjutsu.katas.ohce.adapters.reponses.SequenceOfResponses;
import com.dodevjutsu.katas.ohce.core.Phrase;
import com.dodevjutsu.katas.ohce.core.Response;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.Sequence;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
public class SequenceOfResponsesTest {
private Mockery context;
private Response firstResponse;
private Response secondResponse;
private Response response;
@Before
public void setUp() {
context = new Mockery();
firstResponse = context.mock(Response.class, "firstResponse");
secondResponse = context.mock(Response.class, "secondResponse");
List<Response> responses = new ArrayList<>();
responses.add(firstResponse);
responses.add(secondResponse);
response = new SequenceOfResponses(responses);
}
@Test
public void uses_each_of_its_responses_in_order() {
final Sequence responsesSequence = context.sequence("responsesSequence");
Phrase anyPhrase = new Phrase("anything");
context.checking(new Expectations() {{
oneOf(firstResponse).respondTo(anyPhrase);
inSequence(responsesSequence);
oneOf(secondResponse).respondTo(anyPhrase);
inSequence(responsesSequence);
}});
response.respondTo(anyPhrase);
context.assertIsSatisfied();
}
}
Then I just needed to separately code each concrete response:

package com.dodevjutsu.katas.ohce.tests.unit;
import com.dodevjutsu.katas.ohce.adapters.reponses.ReversingResponse;
import com.dodevjutsu.katas.ohce.core.Notifier;
import com.dodevjutsu.katas.ohce.core.Phrase;
import com.dodevjutsu.katas.ohce.core.Response;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Before;
import org.junit.Test;
public class ReversingResponseTest {
private Mockery context;
private Notifier notifier;
private Response response;
@Before
public void setUp() {
context = new Mockery();
notifier = context.mock(Notifier.class);
response = new ReversingResponse(notifier);
}
@Test
public void echoes_the_reversed_user_phrase() {
Phrase phrase = new Phrase("hola");
Phrase reversedPhrase = new Phrase("aloh");
context.checking(new Expectations() {{
oneOf(notifier).echoReversedPhrase(reversedPhrase);
}});
response.respondTo(phrase);
context.assertIsSatisfied();
}
}
package com.dodevjutsu.katas.ohce.tests.unit;
import com.dodevjutsu.katas.ohce.adapters.reponses.PalindromesResponse;
import com.dodevjutsu.katas.ohce.core.Notifier;
import com.dodevjutsu.katas.ohce.core.Phrase;
import com.dodevjutsu.katas.ohce.core.Response;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Before;
import org.junit.Test;
public class PalindromeResponseTest {
private Mockery context;
private Notifier notifier;
private Response response;
@Before
public void setUp() {
context = new Mockery();
notifier = context.mock(Notifier.class);
response = new PalindromesResponse(notifier);
}
@Test
public void notices_palindromes() {
Phrase palindrome = new Phrase("ana");
context.checking(new Expectations() {{
oneOf(notifier).palindromesRock();
}});
response.respondTo(palindrome);
context.assertIsSatisfied();
}
@Test
public void ignores_non_palindromes() {
Phrase nonPalindrome = new Phrase("koko");
context.checking(new Expectations() {{
never(notifier).palindromesRock();
}});
response.respondTo(nonPalindrome);
context.assertIsSatisfied();
}
}
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:

package com.dodevjutsu.katas.ohce.tests.unit;
import com.dodevjutsu.katas.ohce.core.Dialog;
import com.dodevjutsu.katas.ohce.core.GreetingsSelector;
import com.dodevjutsu.katas.ohce.core.Notifier;
import com.dodevjutsu.katas.ohce.core.Ohce;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Before;
import org.junit.Test;
public class OhceTest {
private Mockery context;
private GreetingsSelector selector;
private Notifier notifier;
private Ohce ohce;
private String userName;
private Dialog dialog;
@Before
public void setUp() {
context = new Mockery();
selector = context.mock(GreetingsSelector.class);
notifier = context.mock(Notifier.class);
dialog = context.mock(Dialog.class);
ohce = new Ohce(selector, notifier, dialog);
userName = "Juan";
}
@Test
public void greets_user() {
String greeting = "any greeting";
context.checking(new Expectations() {{
ignoring(dialog);
oneOf(selector).selectGreeting(userName);
will(returnValue(greeting));
oneOf(notifier).greet(greeting);
ignoring(notifier);
}});
ohce.run(userName);
context.assertIsSatisfied();
}
@Test
public void starts_a_dialog_with_the_user() {
context.checking(new Expectations() {{
ignoring(notifier);
ignoring(selector);
oneOf(dialog).start();
}});
ohce.run(userName);
context.assertIsSatisfied();
}
@Test
public void says_bye_when_told_to_stop() {
context.checking(new Expectations() {{
ignoring(selector);
ignoring(dialog);
oneOf(notifier).sayBye(userName);
ignoring(notifier);
}});
ohce.run(userName);
context.assertIsSatisfied();
}
}
view raw OhceTest.java hosted with ❤ by GitHub
And this is its new code:

package com.dodevjutsu.katas.ohce.core;
public class Ohce {
private final Dialog dialog;
private final GreetingsSelector selector;
private final Notifier notifier;
public Ohce(GreetingsSelector selector, Notifier notifier, Dialog dialog) {
this.selector = selector;
this.notifier = notifier;
this.dialog = dialog;
}
public void run(String userName) {
greet(userName);
dialogWithUser();
sayBye(userName);
}
private void dialogWithUser() {
dialog.start();
}
private void greet(String userName) {
notifier.greet(selector.selectGreeting(userName));
}
private void sayBye(String userName) {
notifier.sayBye(userName);
}
}
view raw Ohce.java hosted with ❤ by GitHub
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.

Thursday, July 14, 2016

Kata: Parrot Refactoring in Java (recorded)

I've recorded myself doing the Parrot Refactoring kata in order to be able to later watch me and detect problems to correct.

This is the recording of what I did: If you decide to watch it, please do it at 2x speed (I still write slowly).

These are the commits after every refactoring step.

You can see the code in this GitHub repository.

Five years ago in Garajeando... (TDD)

This is a post titled "Learning TDD" from 5 years ago that I found last night:
TDD stands for Test-driven development and quoting the Wikipedia definition is
"a software development process that relies on the repetition of a very short development cycle: first the developer writes a failing automated test case that defines a desired improvement or new function, then produces code to pass that test and finally refactors the new code to acceptable standards".
I've been hearing a lot about it since I started programming again. That aroused my curiosity and I'd like to learn how to use it.

These are the resources that I plan to use:
It was very nice remembering when I started this path.

After a lot of practice I'm still learning new things about TDD and enjoying it.

Wednesday, July 13, 2016

An example of using dynamic fact descriptions in Midje

I had these tests written in Midje which had some duplication:

(ns sorting-phones.core-test
(:require
[midje.sweet :refer :all]
[sorting-phones.core :refer :all]
[clojure.java.io :as io]))
(let [expected-output "007334436\n053836111\n076352013\n106093560\n169322377\n172048816\n175914591\n183858586\n365661922\n532648843\n611756075\n631462297\n631988892\n897081845\n898364867\n928002342"
output-file "resources/sorted_phones.txt"]
(facts
"about removing duplicates and sorting the phones in a file and writing them into another file"
(facts
"with a sorter that uses distinct and sort functions"
(let [sorter (->SorterUsingDistinctAndSort)]
(against-background
[(after :facts (io/delete-file output-file))]
(fact
"when there are no duplicated phones"
(sort! sorter "resources/phones.txt" output-file)
(slurp output-file) => expected-output)
(fact
"when there are duplicated phones"
(sort! sorter "resources/duplicated-phones.txt" output-file)
(slurp output-file) => expected-output))))
(facts
"with a sorter that uses a home-made sort function to avoid using distinct function"
(let [sorter (->HomemadeSorterThatAvoidsUsingDistinct)]
(against-background
[(after :facts (io/delete-file output-file))]
(fact
"when there are no duplicated phones"
(sort! sorter "resources/phones.txt" output-file)
(slurp output-file) => expected-output)
(fact
"when there are duplicated phones"
(sort! sorter "resources/duplicated-phones.txt" output-file)
(slurp output-file) => expected-output))))
(facts
"with a sorter that uses a sorted-set function"
(let [sorter (->SorterUsingSortedSet)]
(against-background
[(after :facts (io/delete-file output-file))]
(fact
"when there are no duplicated phones"
(sort! sorter "resources/phones.txt" output-file)
(slurp output-file) => expected-output)
(fact
"when there are duplicated phones"
(sort! sorter "resources/duplicated-phones.txt" output-file)
(slurp output-file) => expected-output))))))
but were providing a nice level of feedback when there was an error:

$ lein midje
FAIL "about removing duplicates and sorting the phones in a file and writing them into another file
- with a sorter that uses a home-made sort function to avoid using distinct function - when there
are duplicated phones" at (core_test.clj:53)
...
...
In my first attempt to remove the duplication in the facts:

(ns sorting-phones.core-test
(:require
[midje.sweet :refer :all]
[sorting-phones.core :refer :all]
[clojure.java.io :as io]))
(let [expected-output "007334436\n053836111\n076352013\n106093560\n169322377\n172048816\n175914591\n183858586\n365661922\n532648843\n611756075\n631462297\n631988892\n897081845\n898364867\n928002342"
output-file "resources/sorted_phones.txt"]
(facts
"about removing duplicates and sorting the phones in a file and writing them into another file"
(doseq [sorter [(->SorterUsingDistinctAndSort)
(->HomemadeSorterThatAvoidsUsingDistinct)
(->SorterUsingSortedSet)]]
(against-background
[(after :facts (io/delete-file output-file))]
(fact
"when there are no duplicated phones"
(sort! sorter "resources/phones.txt" output-file)
(slurp output-file) => expected-output)
(fact
"when there are duplicated phones"
(sort! sorter "resources/duplicated-phones.txt" output-file)
(slurp output-file) => expected-output)))))
I lost the level of feedback the original tests had. It didn't tell which sorter was failing:

$ lein midje
FAIL "about removing duplicates and sorting the phones in a file and writing them into another file
- when there are duplicated phones" at (core_test.clj:33)
...
...
By using dynamic fact descriptions (notice the use of the :midje/description metada):

(ns sorting-phones.core-test
(:require
[midje.sweet :refer :all]
[sorting-phones.core :refer :all]
[clojure.java.io :as io]))
(let [expected-output "007334436\n053836111\n076352013\n106093560\n169322377\n172048816\n175914591\n183858586\n365661922\n532648843\n611756075\n631462297\n631988892\n897081845\n898364867\n928002342"
output-file "resources/sorted_phones.txt"]
(facts
"about removing duplicates and sorting the phones in a file and writing them into another file"
(doseq [sorter [{:description "that uses distinct and sort functions"
:implementation (->SorterUsingDistinctAndSort)}
{:description "that uses a home-made sort function to avoid using distinct function"
:implementation (->HomemadeSorterThatAvoidsUsingDistinct)}
{:description "that uses a sorted-set function"
:implementation (->SorterUsingSortedSet)}]]
(let [sorter-implementation (:implementation sorter)
description (str "with a sorter " (:description sorter))]
(facts {:midje/description description}
(against-background
[(after :facts (io/delete-file output-file))]
(fact
"when there are no duplicated phones"
(sort! sorter-implementation "resources/phones.txt" output-file)
(slurp output-file) => expected-output)
(fact
"when there are duplicated phones"
(sort! sorter-implementation "resources/duplicated-phones.txt" output-file)
(slurp output-file) => expected-output)))))))
I was able to keep the original level of feedback and still remove the duplication:

$ lein midje
FAIL "about removing duplicates and sorting the phones in a file and writing them into another file
- with a sorter that uses a home-made sort function to avoid using distinct function - when there
are duplicated phones" at (core_test.clj:39)
...
...

Saturday, July 9, 2016

Kata: Ohce in Clojure using outside-in TDD with Midje

I did the Ohce kata in Clojure using outside-in TDD with Midje.

I started by writing a nearly end-to-end test for the scenario of running ohce during the morning:

(ns ohce.acceptance-test
(:require [midje.sweet :refer :all]
[ohce.core :refer :all]))
(unfinished hour-fn)
(unfinished read-input)
(future-facts
"about running ohce"
(fact
"during the morning"
(clojure.string/split
(with-out-str
(ohce hour-fn read-input "Pedro"))
#"\n") => ["¡Buenos días Pedro!"
"aloh"
"oto"
"¡Bonita palabra!"
"pots"
"Adios Pedro"]
(provided
(hour-fn) => 8
(read-input) =streams=> ["hola" "oto" "stop" "Stop!"])))
In this test I used Midje's unfinished and provided macros to stub the hour-fn and read-input functions and with-out-str to capture the any printed lines.

Using Midje's future-facts macro I avoided seeing this acceptance test failing every time the tests were run, instead I saw a reminder message like this one:

$ lein midje
WORK TO DO "about running ohce" at (acceptance_test.clj:8)
Next I started testing ohce at the unit level. These are the resulting facts:

(ns ohce.unit-tests.ohce-test
(:require
[midje.sweet :refer :all]
[ohce.ohce :refer :all]
[ohce.test-helpers :as helpers]))
(unfinished select-greeting)
(unfinished read-input)
(facts
"about ohce"
(fact
"it greets the user"
(let [notifier (helpers/fake-notifier)
stop-word "Stop!"]
(ohce select-greeting notifier read-input stop-word ...username...) => irrelevant
(provided
(read-input) => "Stop!"
(select-greeting ...username...) => ...greeting...)
(helpers/args-of-call
:greet :notifications notifier) => [[...greeting...]]))
(fact
"it reverses the user inputs"
(let [notifier (helpers/fake-notifier)
stop-word "Stop!"]
(ohce select-greeting notifier read-input stop-word ...username...) => irrelevant
(provided
(select-greeting ...username...) => irrelevant
(read-input) =streams=> ["hola" "lolo" "Stop!"])
(helpers/args-of-call
:echo :notifications notifier) => [["aloh"] ["olol"]]))
(fact
"it ignores inputs that are blank"
(let [notifier (helpers/fake-notifier)
stop-word "Stop!"]
(ohce select-greeting notifier read-input stop-word ...username...) => irrelevant
(provided
(select-greeting ...username...) => irrelevant
(read-input) =streams=> ["memo" "" "moko" "Stop!"])
(helpers/args-of-call
:echo :notifications notifier) => [["omem"] ["okom"]]))
(fact
"it identifies palindromes"
(let [notifier (helpers/fake-notifier)
stop-word "Stop!"]
(ohce select-greeting notifier read-input stop-word ...username...) => irrelevant
(provided
(select-greeting ...username...) => irrelevant
(read-input) =streams=> ["oto" "ana" "Stop!"])
(helpers/args-of-call
:echo :notifications notifier) => [["oto"] ["ana"]]
(helpers/args-of-call
:palindromes-rock :notifications notifier) => [:no-args :no-args]))
(fact
"it knows when to stop"
(let [notifier (helpers/fake-notifier)
stop-word "Stop!"]
(ohce select-greeting notifier read-input stop-word ...username...) => irrelevant
(provided
(select-greeting ...username...) => irrelevant
(read-input) =streams=> ["Stop!"])
(helpers/args-of-call
:bye-user :notifications notifier) => [[...username...]])))
view raw ohce_test.clj hosted with ❤ by GitHub
For these facts I used a fake implementation of the Notifier protocol:

(defprotocol Notifier
(greet [this greeting])
(echo [this reversed-phrase])
(palindromes-rock [this])
(bye-user [this name]))
which captured the arguments with which the functions of the protocol implementation were being called:

(ns ohce.test-helpers
(:require
[ohce.notifications :as notifications]))
(defn- register-call [func-keyword an-atom & args]
(let [args (if (nil? args) :no-args args)
calls (vec (func-keyword @an-atom))]
(swap! an-atom assoc func-keyword (conj calls args))))
(defn args-of-call
[func-keyword atom-keyword protocol-implementation]
(func-keyword @(atom-keyword protocol-implementation)))
(defrecord FakeNotifier [notifications]
notifications/Notifier
(greet [_ greeting]
(register-call :greet notifications greeting))
(echo [_ reversed-phrase]
(register-call :echo notifications reversed-phrase))
(palindromes-rock [_]
(register-call :palindromes-rock notifications))
(bye-user [_ name]
(register-call :bye-user notifications name)))
(defn fake-notifier []
(->FakeNotifier (atom {})))
(defn output-lines [f & args]
(clojure.string/split (with-out-str (apply f args)) #"\n"))
I could have used Midje's defrecord-openly and provided macros instead but I didn't like having to use defrecord-openly in production code.

Update:
The statement above is wrong. It is not necessary at all to use defrecord-openly in your production code in order to mock a protocol. See how it's done in this other post: Revisited Kata: Using Midje's defrecord-openly to mock a protocol in Ohce.
--------------------------

Another thing to notice in that test is the use of Midje's metaconstants to avoid the facts knowing about the "shape" of data (where it was possible to do it).

Well, those facts lead to the following code:

(ns ohce.ohce
(:require
[ohce.notifications :as notifications]))
(defn- reverse-str [input]
(apply str (reverse input)))
(defn- palindrome? [input]
(= input (reverse-str input)))
(defn- should-stop? [input stop-word]
(= input stop-word))
(defn- respond-to-input [notifier input]
(notifications/echo notifier (reverse-str input))
(when (palindrome? input)
(notifications/palindromes-rock notifier)))
(defn- process-input [notifier input]
(when-not (clojure.string/blank? input)
(respond-to-input notifier input)))
(defn- interact-with-user [notifier read-input stop-word]
(loop [input (read-input)]
(when-not (should-stop? input stop-word)
(do (process-input notifier input)
(recur (read-input))))))
(defn ohce [select-greeting notifier read-input stop-word name]
(notifications/greet notifier (select-greeting name))
(interact-with-user notifier read-input stop-word)
(notifications/bye-user notifier name))
view raw ohce.clj hosted with ❤ by GitHub
Then I started applying TDD on the next level to write ConsoleNotifier, an implementation of the Notifier protocol:

(ns ohce.unit-tests.console-notifications-test
(:require
[midje.sweet :refer :all]
[ohce.test-helpers :as test-helpers]
[ohce.notifications :refer :all]))
(facts
"about console notifications"
(let [notifier (console-notifier {:bye-word "Adios"
:celebration "¡Bonita palabra!"})]
(fact
"greeting user"
(test-helpers/output-lines
greet notifier "greeting") => ["greeting"])
(fact
"greeting user"
(test-helpers/output-lines
bye-user notifier "Juan") => ["Adios Juan"])
(fact
"echoing word"
(test-helpers/output-lines
echo notifier "moko") => ["moko"])
(fact
"celebrating palindromes"
(test-helpers/output-lines
palindromes-rock notifier) => ["¡Bonita palabra!"])))
and the select-greeting function (select-by-day-period in this case):

(ns ohce.unit-tests.day-period-greeting-selection-test
(:require
[midje.sweet :refer :all]
[ohce.greet-selectors :refer [select-by-day-period]]))
(unfinished hour-fn)
(facts
"about hour greeter"
(fact
"during the morning"
(select-by-day-period hour-fn "Koko") => "¡Buenos días Koko!"
(provided (hour-fn) => 6)
(select-by-day-period hour-fn "Koko") => "¡Buenos días Koko!"
(provided (hour-fn) => 8)
(select-by-day-period hour-fn "Koko") => "¡Buenos días Koko!"
(provided (hour-fn) => 11))
(fact
"during the afternoon"
(select-by-day-period hour-fn "Koko") => "¡Buenas tardes Koko!"
(provided (hour-fn) => 12)
(select-by-day-period hour-fn "Koko") => "¡Buenas tardes Koko!"
(provided (hour-fn) => 16)
(select-by-day-period hour-fn "Koko") => "¡Buenas tardes Koko!"
(provided (hour-fn) => 19))
(fact
"during the afternoon"
(select-by-day-period hour-fn "Koko") => "¡Buenas noches Koko!"
(provided (hour-fn) => 20)
(select-by-day-period hour-fn "Koko") => "¡Buenas noches Koko!"
(provided (hour-fn) => 24)
(select-by-day-period hour-fn "Koko") => "¡Buenas noches Koko!"
(provided (hour-fn) => 5)))
Once those tests were passing, I just had to inject a ConsoleNotifier record and the select-greeting function into ohce to make the acceptance test pass:

(ns ohce.acceptance-test
(:require
[midje.sweet :refer :all]
[ohce.ohce :refer :all]
[ohce.greet-selectors :refer [select-by-day-period]]
[ohce.notifications :refer [console-notifier]]
[ohce.test-helpers :as helpers]))
(unfinished read-input)
(unfinished hour-fn)
(let [notifications-config {:bye-word "Adios"
:celebration "¡Bonita palabra!"}]
(facts
"about running ohce"
(fact
"during the morning"
(let [stop-word "Stop!"
any-hour-during-morning 9
notifier (console-notifier notifications-config)
select-greeting (fn [name] (select-by-day-period hour-fn name))
ohce (partial ohce select-greeting notifier #(read-input) stop-word)]
(helpers/output-lines
ohce "Pedro") => ["¡Buenos días Pedro!"
"aloh"
"oto"
"¡Bonita palabra!"
"pots"
"Adios Pedro"]
(provided
(hour-fn) => any-hour-during-morning
(read-input) =streams=> ["hola" "oto" "stop" "Stop!"])))
(fact
"during the afternoon"
(let [stop-word "Stop!"
any-hour-during-afternoon 16
notifier (console-notifier notifications-config)
select-greeting (fn [name] (select-by-day-period hour-fn name))
ohce (partial ohce select-greeting notifier #(read-input) stop-word)]
(helpers/output-lines
ohce "Lolo") => ["¡Buenas tardes Lolo!"
"opip"
"Adios Lolo"]
(provided
(hour-fn) => any-hour-during-afternoon
(read-input) =streams=> ["pipo" "Stop!"])))
(fact
"during the afternoon"
(let [stop-word "Stop!"
any-hour-during-night 1
notifier (console-notifier notifications-config)
select-greeting (fn [name] (select-by-day-period hour-fn name))
ohce (partial ohce select-greeting notifier #(read-input) stop-word)]
(helpers/output-lines
ohce "Juan") => ["¡Buenas noches Juan!"
"oko"
"¡Bonita palabra!"
"Adios Juan"]
(provided
(hour-fn) => any-hour-during-night
(read-input) =streams=> ["oko" "Stop!"])))))
I also added two other scenarios (running ohce during the afternoon and during the night).

A curious thing to notice is that I couldn't use partial with select-greeting if I wanted the provided macro to work with hour-fn, that's why I used a lambda instead. Due to the same problem I also had to wrap the read-input function inside a lambda.

Then to be able to really run ohce on the console I just had to write a real hour-fn function (tested on the REPL):

(ns ohce.clock
(:import (java.util Calendar)))
(defn hour []
(.get (Calendar/getInstance) Calendar/HOUR_OF_DAY))
view raw clock.clj hosted with ❤ by GitHub
and create a -main function (read-input is just the Clojure's read-line function):

(ns ohce.core
(:require
[ohce.ohce :refer :all]
[ohce.clock :as clock]
[ohce.notifications :refer [console-notifier]]
[ohce.greet-selectors :as greet-selectors]))
(defn -main [& args]
(let [select-greeting (partial greet-selectors/select-by-day-period clock/hour)
notifier (console-notifier {:bye-word "Adios"
:celebration "¡Bonita palabra!"})
ohce (partial ohce select-greeting notifier read-line "Stop!")]
(ohce (first args))))
view raw ohce-core.clj hosted with ❤ by GitHub
This is an example of running ohce:

$ lein run Lolo
¡Buenas tardes Lolo!
hola
aloh
vino
oniv
akka
akka
¡Bonita palabra!
Stop!
Adios Lolo
$
view raw running_ohce hosted with ❤ by GitHub
I committed after each passing test and each tiny refactoring, so that you can follow the process if you feel like. You can check the commits step by step here.

You can find the resulting code in this GitHub repository.

Doing this kata helped me to learn a lot of new things about Midje.