This post originated from an RSS feed registered with Ruby Buzz
by Bryan Liles.
Original Post: Increasing your testing vocabulary
Feed Title: Smarticus
Feed URL: http://smartic.us/feed/atom.xml
Feed Description: Ramblings of a ruby hacker
On your journey to writing better tests, there are a quite a few hurdles to overcome. One of those hurdles is improving your your vocabulary.
One day I noticed I was writing better tests. I also noticed that I was actually enjoying the testing piece. What turned it around for me was getting the vocabulary right.
Let’s start at the beginning:
123
deftest_new_assigns_carend
This is where I started. As you can most likely infer from the method signature, I was attempting to test that new assigns car. What? This doesn’t really make any sense, but there are plenty of tests floating around right now named in this fashion. We can make this example better, so let’s give it another try:
123
deftest_get_on_new_should_assign_to_carend
This is better. We can now pretty accurately determine that we are testing ‘get’ on something called ‘new’ and it should assign something. If I mentioned that is was a functional test for a Ruby on Rails controller, you most likely would guess what I’m aiming for here. One lesson that you might pick up here is that is OK to give your tests extremely long method names. When it comes down to it, the more verbose the better, right? It might sound good at first, but extremely long test method names make it difficult to read your test files, and do nothing for helping your test class organization.
So why don’t we take another stab at it? This time, we’ll incorporate Shoulda, as I use this on a daily basis:
12345
context "get on new"do setup { get :new } should_assign_to :carend
Now, we are getting to a good place. Our test code is becoming easier to read since we have introduced contexts. Contexts are one of the great points of Shoulda and RSpec alike. We can read it like, “after doing a get on action new, an instance variable car should have something assigned”. At first glance, this reads pretty easily, and for quite some while, I was stuck in this place. It isn’t a bad place, but there is still room for improvement. So, now I’m progressed my tests a bit further:
12345
context "requesting the new car form"do setup { get :new } should_assign_to :carend
Notice the simple language change from what we are physically doing to what we are trying to accomplish. These types of changes don’t make your tests run any faster or better, but they do make them more readable and are great for increasing your testing vocabulary. We should all strive for great readability, but don’t let the quest for English like tests take you to far from what you are actually doing: writing tests.
We can now work on improving our vocabulary even further with another example. Testing ‘create’ actions for Rails controllers seems to be a straight forward set of tasks generally.
12345
context "creating a new car"do setup { post :create, :car => {:name => "My new Car"}} should_redirect_to "car_url(@car)"end
Finished? Well not quite. What happens if the car isn’t created correctly? We need some more test code:
12345678910111213
context "creating a new car"do context "successful creation"do setup { post :create, :car => {:name => "My new Car"}} should_redirect_to "car_url(@car)"end context "failed creation"do setup { post :create, :car => {:name => ""}} should_assign_to :car should_respond_with :success should_render_template "new"endend
Now, we have a more complicated example. Using nested contexts, we can group tests that are related together. If you text editor or IDE has code folding capabilities, you only have to focus on what you are doing, and you still won’t lose site of your goal.
In most cases, we might stop here, but there are still other things to think about. With Ruby on Rails, it is fairly simple to create resources that can be retrieved and manipulated in a REST like manor. We can also state the format of the reply we want just by manipulating the URL. So, in a nutshell, this means if you exposing HTML along with JSON or XML or some other data format, you’ll need to test those as well.
12345678910111213141516171819202122232425262728
context "creating a new car"do context "successful creation"do context "expecting HTML"do setup { post :create, :car => {:name => "My new Car"}} should_redirect_to "car_url(@car)"end context "expecting XML"do setup { post :create, :car => {:name => "My new Car"}, :format => "xml"} should_respond_with :201 should "respond with properly formatted xml"do assert_xml_is_good_and_stuffendendend context "failed creation"do context "expecting HTML"do setup { post :create, :car => {:name => ""}} should_assign_to :car should_respond_with :success should_render_template "new"end context "expecting XML"do setup { post :create, :car => {:name => ""}} should_respond_with :400endendend
We have lots of tests here. Testing positive and negative conditions with multiple formats. And notice that is no implementation here… so we did some TDB (test driven blogging) as well. From here, only your imagination or impending deadlines can limit you.
So, what can we learn from all of this? I’m sure there are multiple lessons buried somewhere in here, so I’ll let you pick the good parts from the not so good parts. I only have one goal, and that is get everyone to start expressing intent rather than the intended action when testing. If get at least that, my job is done for the day. Just remember to go after many small wins instead of swinging from the fence every time.