Behavior Driven Outside In Development Explained, Part 2

In Part 1 of this series, I walked through the development cycle of the "user views candidates" feature.  In this article, I am going to implement the "user searches for candidates" feature, focusing on unit tests and refactoring. I will not show all the detailed steps - for a step by step walkthrough on implementing features following the BDD Outside In way, please see Behavior Driven Outside In Development Explained, Part 1.

Let's first write the cucumber feature: 

And run the cucumber test to see it fail. The test fails because it tries to fill in a search term in the "search" text field, which is not on the page yet. We will add that part of the page:

We'll run the test, create a new route for searching candidates, and create the "search" action on the candidates controller.  We are adding two exposures to get the search term parameter and ask the model the search for candidates. Again the test will fail, telling us that we need to implement the search functionality on the model level.

Screen_shot_2011-10-08_at_2

Now we've dropped to the inner BDD circle again. Let's first write a spec to express how we expect the search to function. The spec has 7 examples, covering cases with one word, two words, partial words, empty string, and search with a multi-word last name. It is much easier and faster running to provide comprehensive test coverage for the candidate search function on the unit test level (Rspec) than to do it on the integration test level (Cucumber).

Let's first let the test fail:

Screen_shot_2011-10-08_at_2

It took me a few tries to let the tests all pass - I settled with a solution that I would create a new database column "full_name" which is automatically updated when candidate is saved with concatenated first name and last name.

We will run "rake db:migrate" and "rake db:test:prepare" to migrate the development and test databases. The candidate unit tests now will all pass - also telling us that though we changed the way full_name is implemented, we didn't change its behavior.

Screen_shot_2011-10-08_at_3

Now let's jump out to the integration / cucumber test circle, and this time it will just pass. We can run the whole test suite again to insure that we did not introduce any regression bugs - one of the best benefits for the BDD, is that most bugs are caught right when they are introduced, and they are much easier to fix when the programmer still has the domain and context fresh in mind. In fact I can remember when I didn't do much testing it was not uncommon for me to spend at least half of my time fixing bugs (many bug fixes introducing new bugs!), while at Hashrocket I spend almost all my time implementing features.

Screen_shot_2011-10-08_at_4

Now that all tests pass, it's time for refactoring ("Refactor in the Green", as the saying goes). The "list candidates" and "search candidates" features share a view, but we are relying on the presence of the search_result exposure to differentiate showing search results or all candidates and this is quite ugly. A better solution would be to make search a scope on the candidate model that just passes through if no search term is present. We can chain the search scope within the candidates exposure in the controller and remove the condition in the view.



The code is now much more succinct and the logic is simpler. We will run the complete test suite again to make sure that everything works and they do. The test suite we built up along the way gives us great confidence in the refactoring - so that we actually do it often and keep the code base clean.

This is it! Leave some comments and let me know what you think.

The source code is here on Github. 

Behavior Driven Outside In Development Explained, Part 1

 
(Note: part 2 of this post is here.)
It has been a while since I joined Hashrocket this July, and I have learned a lot from working with folks here. In particular, Hashrocket's behavior driven, outside-in way of developing software has been an eye opening experience. 

Behavior Driven Development (BDD) is an agile software development process that use natural language to describe desired the outcome of features, and use it to serve as integration and regression tests; It follows an "outside-in" approach to drive software development guided by tests.

The following figure (from The Rspec Book, Figure 1.1) explains the development cycle of BDD. The outer circle is driven by integration tests and the inner circle is guided by unit tests. We will start from the outer circle, write an integration test that would fail (no implementation yet!), move it forward until it reaches the domain model, where we will "drop down" to the inner circle - write unit tests for the model, see them fail, code the implementations to make them pass, and refactor if necessary ( this fail -> succeed -> refactor cycle is also called the red -> green -> refactor cycle as mentioned in the figure below). After the inner circle is complete, we will "jump out" to the outer circle and let the integration test drive us to complete the given feature. 


Screen_shot_2011-10-06_at_10

I will use an example of building a recruiting candidate management application to show how BDD is done in Hashrocket. I will walk through the process of building two features - user viewing the list of candidates and searching candidates.


We will build the "user views candidate list" feature from a blank slate. Following the BDD way, we will first write the integration test - a cucumber feature file to state what we expect the interaction to be.

We haven't implemented this feature yet, so if we run cucumber it will fail and it fails at where it tries to generate the three test candidate objects. In Hashrocket, we use fabrication (developed by our very own Paul Elliott) as the object generation library for both integration and unit tests. Here, Cucumber is complaining that it cannot find "fabricators" to generate candidate objects - because we haven't defined them yet (well, we haven't done anything just yet). But not to worry, the point of "Outside In" development is that the test will tell us exactly what to do step by step until the feature is implemented and the test passes. 


Image


Following the failure message, we will create a fabricator:

Test Driven Development (a subset of Behavior Driven Development) tells us to just implement enough code to make the test pass or get a new error. We will resist the urge to go further other than just defining the fabricator. Let's run the the cucumber command again. And this time, the test tells us that there's no class for "candidate". 
 

0image


We will generate a "candidate" class, and see how the test tells us to do next: 

Unknownname


The test is telling us now that our next step should be to create a migration to create the database table for "candidates". 

we will run migrations for both the development and test databases, and run the cucumber test again: 

0unknownname


This time it's telling us that cucumber doesn't know how to map "the candidate list page" to a path. Let's create this mapping in the feature/support/paths.rb file. We will add the regular expression /the candidate list page/ and point it to "candidates_path", and create a new route to point to that path. 

We will run the test again - the route we just added is trying to route our request to CandidateController, which, at this time is not yet defined. 
 

1unknownname

We will generate the controller and the logic in the controller to get all the candidates. Instead of creating an "index" action and put candidates in an instance variable, In Hashrocket, we use  decent_exposure  (by our very own Stephen Caudill) in controllers to maintain states - please see this article where Stephen explains the rationale behind it. For now, we will just expose all candidates to be available in the view. We do not need to create an index action in the controller because decent_exposure takes care of it automatically.

We will also create a view to show a list of candidates in a table (we use haml and sass on the front end). 


Note that in the view we are calling candidate.full_name for the first column because that column needs to be the full name of the candidates. We do not have the full_name method defined for the candidate model yet therefore it will fail again.

2unknownname


Still remember the circles? Now we are dropping down to the inner circle of domain models. To test drive this level of the application we will rely on Rspec and unit tests. We will create a spec file for the candidate model and put in our expected behavior the "full_name" method: 

Just like on the outer circle, in this inner circle we also follow the "red - green - refactor" cycle. So we will first run it to see it fail. 

3unknownname



Then we will implement the full_name method in the candidate model, run the spec to see it pass. 



4unknownname

This is a very small piece of implementation so there is really no need for refactoring at this point - we will skip the refactor step (I will cover refactoring in part 2 of this article). Now that we completed the inner circle of model and unit testing, let's jump out to the outer circle and run our cucumber test:

5unknownname


This time the test tells us that it cannot find "Bart Simpson" in the first cell in the table, and this is most likely a data related issue - we will throw in the "show me the page" line to see the screen right where it failed. 

Looking at the page, it looks like that the result table of the 3 candidates are not sorted by first name as we wanted.

Screen_shot_2011-10-04_at_10

We will go to the candidate model and add the sorting into the exposure of the candidates controller:

We run the cucumber test again and this time it passes!

Screen_shot_2011-10-04_at_10

As the last step, we will open up the browse and actually manually test this feature to make sure they work as desired - in this case we will need to create some candidates in the console first then we can see them listed, nicely sorted. 

 

 

 

Snake game with Faye

I spent some time over the weekend working on the muli-player Snake game, with a lot of javascript on the front end and a rack Faye server on the back end. It took me some time - quite a few things were not trivial.

First, the Faye server's set up. Over at Railscast, Ryan Bates has an episode on "Messaging with Faye", but I found it redundant to have faye running on its own server in addition to the web server. Faye can be used as middleware and it's very easy to drop into an existing Rack app. In my case, I am using Sinatra, so the config.ru looks like this: 

To run the server, do 

rackup config.ru -s thin -E production

On the front end, Paul Jensen has some nice javascript code that I took as the base. I changed his code quite a bit, mostly refactoring it into models so I can expand it to a multi-user game. 

The game's rules are pretty simple - the canvas refreshes every 1/10 of a second; the snakes keep traveling unless told to turn; when a snake eats food its body gets longer by 1; the game ends when a snake "eats" itself. 

Initially my thought was to have the server push out every single frame to the clients to ensure synchrony but it turned out to be too much data being passed around. I ended up with client communicating to the server only when its snake turns, eats food or dies, and sending over the "world" to update the other clients (again, for better synchrony). This works much better than the first approach and is also architecturally easier - there is no logic on the server side other than a broadcaster and clients handle their own states until being told to sync up with an incoming state. 

I deployed the game on Heroku - they use Thin as their server too it works. However, I have found that the latency is quite bad between browser clients and the server. It does not feel very real time. If I were to increase my Dynos or Workers the the messaging pipes would just break - I can see how it is so with Dynos - they are separate nodes after all, but I thought Workers are supposed to work off tasks on the same queue? 

To play with this game, clone my github repo and run it on local. The game is still very much unfinished, and there are definitely bugs. Submit patches or fork it as you please. 

The code is here

 

co-hacking this weekend?

I am going to do a hack + learn session this weekend to develop a Ruby event based / async server for the Snake game. I looked at several technologies in this space and decided to give Faye a try - Node based solutions are pretty cool too, and I’ll use another time to do a project on that.. 

What Faye really impresses me is that it’s rack based, so it is trivial to integrate it with Rails applications, and can easily leverage the other awesomeness in the Ruby ecosystem (the language, gems, hosting, etc). 

On the front end, I am going to use HTML5’s web socket and canvas to control the snake.. should be interesting. 

I’ll be doing this with some people together this weekend. if you want to join please tweet me @knwang - we’re all remote and planning just to use IRC to share and discuss. 

Some related reading: 

http://railscasts.com/episodes/260-messaging-with-faye

http://faye.jcoglan.com/

http://diveintohtml5.org/

 

RailsConf Day 4

Keynotes of Day 4 were some of the best:  Check out Corey Haines keynote here on becoming awesome. and if you are a manager and claims that people are your best assets you need to check out how to optimize for developer’s happiness. Glenn Vanderburg gave an insightful presentation comparing software engineering with other engineering disciplines - are we craftsmen, engineers, architects, artisans all of the above, or none of the above? Watch it, it’ll be worth your time. 

Yehuda Katz / @wycats talked about “Building Rails Apps for the Rich Client”. I thought he was going to talk quite a bit on SproutCore but he barely touched it. I could not find the presentation for this so here is a summary: 

  • Rails can be very useful in making APIs for machine to machine communications. 
  • Q: If all I need is JSON, why do I need Rails?  Isn’t something like Sinatra better? 
  • A: Rails is not trivial, a lot is hidden. It’s a rich abstraction of HTTP. It takes care of a lot for you: sessions, cookies, security, standards, constraints, caching, etc. If you move away from Rails you have to do these yourself. and it’s hard to make things work well together. Rails does a lot of awesomeness underneath. 
  • Some rules on API design: 1) root has to have resource keys 2) no nested URLs. 3) nested object should follow 1) and 2)

Passing JSON back to the rich client to have a JavaScript templating engine (SproutCore, Backbone.js, Mustache.js etc) to render is a trend.. and Yehuda is making the point that Rails is still a good choice for building APIs. 

I picked up some nice tips from the “Bridging the Gap between Rails and JavaScript in Rich Client Apps” session (presentation here). A very brief summary - how to reuse code across Ruby and JavaScript in your app? 

  • Write JavaScript code in CommonJS modules
  • Use IncludeJS to use them in Ruby
  • DRY!

Matt Kelly / @mkelly12 gave the presentation on Backbone.js, a popular JavaScript templating engine. As I said before, having a MVC front end structure is a trend building rich client apps… and it can really do magic.. check out the Chop app that backbone guys just released (make sure to drag and drop on the code). The presentation itself is a lot of fun too!

FINAL WORDS: 

I got a lot from this conference - it has certainly been information overload, but I came back home excited about all the people doing interesting projects. Met many people who I have known but never got to meet in person. It is very true that open source is all about the community, and RoR has the best community there is.. it almost feels like a fraternity. 

RailsConf Day 3

Day 3: 

Dr. Nic  @drnic ‘s opened up the morning keynote to talk about Ruby’s history, but it’s really from Engine Yard to get folks to use JRuby and Rubinius.

Aaron Patterson / @tenderlove (only person in both Ruby core team and Rails core team) gave an “Intense” keynote - you have to see it to know what’s in it - it’s definitely more than you think. :-) He dived into some under the hood stuff in Rails 3.1 improvements  - prepared statement caching, new serialization strategies, streaming response, reduced middleware stack. 

The “Controlled Choas” session is a case study of NASA transitioning from legacy system (yes, Java is now considered Legacy now!!). The reasons behind the switch are 1) they want a easier interface (SOAP to REST) and 2) They started to lose developers because nobody wanted to work on old technologies. They switched to JRuby to make use of the JVM. The result: happy developers, team moving much faster, much more maintainable code base. 

The two BoF sessions I went to were awesome -

Ryan Bates / @rbates (of the Railscast fame) led a walkthrough on the Cancan gem 2.0 version (for role based authorizations). It’s still under development.. but is a huge step over 1.0. I have actually evaluated Cancan for one of my projects but found it somewhat limited on the finer level of controls. 2.0 would change that - I like where this project is going. 

Yehuda Katz gave a presentation on SproutCore. This is one of the things coming up that I am really excited to dig into (the other is the Ruby EventMachine). It has matured much from when I looked at it last time - for one.. there’s online documentation now! I think desktop like web applications are the way to go, and it’s really cool to have a framework like SproutCore to make things easier. There was a heated discussion on SproutCore vs Backbone.js and Yehuda’s message was: SproutCore has everything Backbone (mostly observers) has and more, and the argument that SC is too big is not so important in the bigger scheme of things. 

RailsConf - Day 2

My intention was to write one blog post per day chronicling my experiences but that turned out to be very difficult - sessions ran into late evenings (some BoF sessions ran as late as 10pm, but were really worthwhile), plus the casual chatters and the happy hours.. recovering from jetlag from Asia didn’t help either.. so didn’t get to write on it until today. 

But here it goes: 

Day 2:

@dhh gave the keynote in the morning to unveil Rails 3.1 RC - asset pipeline, jQuery default, native CoffeeScript + Sass support and all. No big architectural changes but solid steps to make Rails more organized. He also hinted that pjax and backbone.js could be there in Rails 3.2 (presentation layer MVC is a recurring topic this year).

Jeff Casimir / @j3 gave a very nice session on “Fat Models aren’t enough”, where he talked about better organization of code in the models, the presenter pattern (don’t just pass a bunch of instance variables into the view, pack them up in a presenter class), scopes (or the no need of ) with lots of code snippets. Presentation here

Another session that I found really good is “Confident Code” by Avdi Grimm / @avdi. Presentation here. He talked about the Nil objects being over used in Ruby is so true. The idea of killing all nil in the code is refreshing (therefore eliminating all the checking).  I’ll certainly incorporate that in my code.

Eric Ries, the godfather of the “lean startup” movement gave the evening keynote. I’ve seen him speak before, but it makes me think every time. If you are into building a software product start up, his work is a must read. Video is here

RailsConf Day 1

Day 1 of RailsConf is the day of tutorials, which turned out to be quite good. 

I attended the HTML5 session by @subelsky in the morning and learned a bunch. Like everybody else, I have heard about the HTML5 buzz for a while, but never got deeper into it than scratching the surface. The session ran in the form of 13 labs, and drawing, sockets, native storage, and of course native media support are the highlights. In fac you can do many cools things with just html5 now.. such as this game. The presenter did a great job packaging the material as a series of easy to follow labs. You can get them here and follow it. It will all just take a couple of hours. 

I went to “Building Bulletproof Views” in the afternoon by the LivingSocial guys.. Solid presentation and I learned quite a lot.. I tend to agree with them that the rails view layer is wild west and is probably the least organized part of the framework, and I didn’t see much “best practices” before on this layer - good work that they put this together. Interestingly, I found half of the issues are made really easy with using an authorizing framework like Compass. They also talked about some tools like formastic, goose (a gem for tab navigations), responsive design and the mobile web. Presentation is here

Also, the Envy Labs guys gave sessions on Rails for Zombies (beginner’s course to Ruby on Rails) and Rails Best Practices. Check them out. 

Finally, IgniteRails is awesome! @drnic is hilarious