Rock Scissors Paper: TDD as if you meant it
I decided to spend a bit of time on Saturday having another go at writing Rock Scissors Paper while following Keith Braithwaite’s TDD as if you meant it exercise.
We previously did this exercise at a coding dojo but I wanted to see what happens when you code for a longer period of time with this exercise since we typically only code for maybe a couple of hours at a dojo.
I decided to also checkin the code I was writing into Git after every single change I made - an idea I originally learnt from Dan North, and the code is all on github.
What did I learn?
-
I was coding for maybe 4 or 5 hours and checked in about 120 times which is far more frequently than I would do normally although perhaps it shouldn’t be.
I thought it would be quite distracting to have to check in that often but the checkin messages ended up becoming a stream of consciousness of what I was thinking which proved quite useful on a few occasions when I got side tracked a bit and retraced my steps in the history to find out what I was supposed to be doing - Git was pretty much acting as my sounding board since I didn’t have a pair to work with on this exercise. I’d definitely try this out again and as I’ve mentioned a few times I want to try it out in a coding dojo some time. -
While trying to remove some duplication that had worked its way into the code I realised that the template method would be the easiest way to remove that duplication and as I mentioned in an earlier post, having functions/actions in C# makes the implementation of this pattern a bit simpler: ~csharp public abstract class ThrowBase : IThrow { private readonly Func<IThrow, bool> beatenBy; protected ThrowBase(Func<IThrow, bool> beatenBy) { this.beatenBy = beatenBy; } .... public bool Beats(IThrow aThrow) { return IsDifferentThrowTo(aThrow) && !beatenBy(aThrow); } } } ~ ~csharp public class Scissors : ThrowBase { public Scissors() : base(weapon => weapon.BeatsScissors()) { } ... } ~ If we didn’t have functions then we’d need a method on 'ThrowBase' which we would need to implement in each of its sub classes. As it is we can just pass a function into the constructor which does the same job.
-
Another thing I found quite interesting about this exercise was that I was seeing more easily where methods didn’t seem to belong on an existing object and I ended up creating a new object which described the interaction - chatting about the code with Liz she pointed out that I was probably creating domain events which we had discussed a few days earlier in book club. Once I started thinking about this interactions as 'domain events' the naming of them seemed to be much more obvious - for example after this conversation with Liz I realised that the interaction between two 'Throws' would be a 'ThrowDown' and that there would probably be a 'ThrowDownAdjudicator' to decide the outcome of a 'ThrowDown'.
-
I found it quite difficult to follow the rules of the exercise when I realised that I wanted to return a 'ThrowDownResult' which would indicate whether there was a winner for a 'ThrowDown' and if so who it was. I couldn’t see a way to write all of this code in the test since I had already created the class previously so I ended up writing this code straight onto the class. I think I probably extracted to a class too early on some occasions instead of waiting for a more complete version of a method to be written before moving it. Next time I think I’d wait until I’d completed all the examples /tests I want to write around a particular method before moving it onto a class.
About the author
I'm currently working on short form content at ClickHouse. I publish short 5 minute videos showing how to solve data problems on YouTube @LearnDataWithMark. I previously worked on graph analytics at Neo4j, where I also co-authored the O'Reilly Graph Algorithms Book with Amy Hodler.