In the first post of this series I summarised my introduction to Growing Object Oriented Software Guided by Tests (GOOS), and why I decided to revisit it in late 2013. This second post summarises my first attempt at the Auction Sniper worked example in JRuby, how things didn't work out too well, and my approach to dealing with these issues. I'm still to complete the worked example in JRuby, but as you'll see, I took a 'brief' detour...
The auction sniper
The worked example in GOOS is based around the auction sniper; an application that bids in auctions at Southabee's - a make believe auction house. The auction sniper uses Southabee's XMPP interface for all communication. The worked example follows the typical agile approach of iteratively adding or bulking out features, whilst ensuring existing features continue to work. Therefore, an end to end test suite provides both regression testing coverage, and examples of new features to be implemented. This end to end test suite uses a Window Driver to exercise the auction sniper, and a fake auction service which mimics Southabee's actual service, and is used to ensure the auction sniper sends and reacts appropriately to XMPP messages.
The walking skeleton
The worked example follows Steve and Nat's advise of starting product development with a walking skeleton: 'an implementation of the thinnest possible slice of real functionality that we can automatically build, deploy and test end-to-end'.
I view walking skeletons as a stereotype of the more common spike solution, and they're typically used to explore, inform, and identify risks before investing significant effort implementing the whole application based on perhaps premature assumptions.
My initial walking skeleton
For my JRuby Auction Sniper walking skeleton, I envisaged
- An end to end test written using RSpec
- The auction sniper using JRuby interoperability to expose a basic Swing UI
- A JRuby FakeAuctionServer to mimic Southabee's in end to end tests
- Vines - a lightweight Ruby XMPP service deployed using Docker
This first end to end test would prove the test suite could interact with and query the auction sniper's user interface, and that the auction sniper could communicate with the fake auction service sufficiently to join an auction and present it's failure to win said auction - at this stage, it wouldn't even try to bid.
It's worth mentioning at this point, that I originally hoped to pair program this JRuby implementation with Andy Henson, but just as we started, he landed a large customer project which limited his ability to work on the sniper. I thus started this walking skeleton on my own. I hope to start collaborating with Andy very soon.
It started so well
I found it easy to convert the first end to end test, and its supporting application runner and window driver classes to Ruby / RSpec. I had to expend a little brain power to manually download and reference appropriate WindowLicker binaries but within a couple of hours I was confident the tests were correctly exercising the user interface.
It was upon starting development of the fake auction service that I started to encounter more significant issues. Downloading the Smack library, and referencing it within JRuby was a little tricky. The Smack API had changed quite a bit since the book was authored (2009), and several examples on their website contradicted each other and the code presented in GOOS.
The next step; wiring the auction sniper up to to Smack proved a little too much for my current approach. I found the syntax and semantic differences between threading in Ruby, JRuby, and Java challenging. Although I did manage to get Smack to attempt to communicate with the Vines XMPP service, I started to lose confidence in the similarities between the book's code and my ruby interpretation.
You may have noticed I used the phrase 'attempt to communicate' in the above paragraph. Unbeknown to me, some of the Smack API changes were in response to a security vulnerability. Little did I know I'd end up helping the Smack developers test even further changes later in my adventure. The Smack library refused to successfully connect to Vines, and I spent significant effort researching TLS, certificates and related technical concerns which I felt distracted me from what I hoped to learn from the worked example - how to better test drive object oriented code and design. I tried numerous solutions from StackOverflow and Smack's forum, but none worked. Worse still, I was in uncharted waters using JRuby, and whilst improbable, I also couldn't discount this as a possible cause.
I lost a fair few evenings trying to diagnose this issue and finally, clutching at straws decided that trying a more 'ruby-esc' XMPP client might resolve the issue.
Blather to the rescue?
Having reviewed a few Ruby XMPP clients, I chose and referenced Blather in my Gemfile. I had chosen it primarily on it's appearance as the most popular, active and mature offering in the Ruby space. What's more, I easily managed to connect the AuctionSniper to Vines. Whoop - job done?
Unfortunately, not quite. I'd previously stated a lack of confidence with my interpretation of the books integration code with a slightly changed Smack API, and JRuby language porting. Well, with this significant swapping of a core dependency, I lost a near terminal chunk of confidence in my JRuby implementation.
The Java detour
To recap, I'd arrived at a situation where my first end to end test nearly passed, but with a dependency, and its integration considerably different to that in the book. I'd already spent several evenings trying to debug my interpretations of the books code, unsure whether failures were due to the books example code, my interpretations thereof, the dependencies I had chosen, or a combination of all three. All this effort spent, and I was still in the walking skeleton phase... I had little appetite to follow this recipe for the rest of the worked example.
As I saw it, I had two options.
- Throw caution to the wind, and go with the Ruby tooling. It had worked up to this point but, based on recent experiences, I had real reservations that my interpretations might at best prevent me from learning some of the subtle lessons as intended by the authors. At worst, I might become stranded; unable to map my design to that of the book. I might not be able to complete the example.
- More conservatively, to alter course to stay as close to the book as possible which would mean attempting it in Java rather than Ruby. In choosing this option, I would no doubt maximise my exposure to Steve and Nat's intended lessons in emergent OO design, but it would mean sacrificing this adventure as a way of improving my Ruby development skills.
Luckily, I'm well aware of the sunk cost fallacy, and was quite happy to throw away the code I'd spent maybe 20 hours writing. I knew I'd gained much value in learning, and little in actual code. Anyhow, the code would still exist in a Git repo. I really could choose the option that made most sense to what I wanted to achieve. And the option I chose was.. Both!
At this point, I mothballed my JRuby implementation, and with the insight already gained, I started the worked example again in Java. Whilst ultimately successful, my attempt in Java wasn't exactly plain sailing, and it will be the subject of the next post in this series.
The next stage of my adventure is to dust off the JRuby implementation, hopefully with the assistance of Andy Henson.