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 worked example in GOOS is based around the auction sniper; an application that bids in auctions at Southabee's - a make believe auction house. This auction sniper uses Southabee's XMPP interface for all communication. The worked example follows a typical agile approach of iteratively adding features, whilst ensuring existing features continue to work. The book advises a particular test suite to provide both regression coverage and examples to guide implementation of new features included end to end functional tests. These end to end tests use a Window Driver to exercise the auction sniper, and a fake auction service which mimics Southabee's XMPP interface used to ensure the auction sniper collaborates appropriately with the real service.
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 great approach to explore, inform, and identify risks before investing significant effort implementing a whole application.
An end to end test written using RSpec
A JRuby FakeAuctionServer to mimic Southabee's in end to end tests
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 limiting 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.
I found it easy to convert the first end to end test, and its supporting application runner and window driver classes to JRuby / 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. The book recommends the Smack client as an XMPP client library, but I found downloading and referencing it within JRuby tricky. Further, the API had changed quite a bit since the book was authored in 2009, and several examples on their website contradicted each other and the code presented in GOOS.
Wiring the auction sniper up to Smack proved a little too challenging for my current approach. I found the syntax and semantic differences between threading in Ruby, JRuby, and Java confusing. 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.
Having reviewed a few Ruby XMPP clients, I chose and referenced Blather in my Gemfile. I chose it primarily due to it's popularity and active community. Further, 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. This significant swapping of a core dependency, trigged loss of confidence in my JRuby implementation.
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 approach 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. I really could choose the option that made most sense to what I wanted to achieve. And the option I chose was.. Both!
I've mothballed my JRuby implementation, and with the insight gained, reimplemented the worked example 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.