Record of experiments, readings, links, videos and other things that I find on the long road.
Registro de experimentos, lecturas, links, vídeos y otras cosas que voy encontrando en el largo camino.
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.
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
and then write the tests against this unimplemented fake.
This are the new Ohce tests:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
I wasn't happy with the resulting tests for the Ohce class:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Then I just needed to separately code each concrete response:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
"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.
I had these tests written in Midje which had some duplication:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
but were providing a nice level of feedback when there was an error:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
In my first attempt to remove the duplication in the facts:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
I lost the level of feedback the original tests had. It didn't tell which sorter was failing:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
By using dynamic fact descriptions (notice the use of the :midje/description metada):
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
I was able to keep the original level of feedback and still remove the duplication:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
I started by writing a nearly end-to-end test for the scenario of running ohce during the morning:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Next I started testing ohce at the unit level. These are the resulting facts:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
For these facts I used a fake implementation of the Notifier protocol:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
which captured the arguments with which the functions of the protocol implementation were being called:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Then I started applying TDD on the next level to write ConsoleNotifier, an implementation of the Notifier protocol:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
and the select-greeting function (select-by-day-period in this case):
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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):
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
and create a -main function (read-input is just the Clojure's read-line function):
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.