Skip to content


Increasing your testing vocabulary

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.

<p>Let&#8217;s start at the beginning:</p>
1
2
3

def test_new_assigns_car
end
<p>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&#8217;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&#8217;s give it another try:</p>
1
2
3

def test_get_on_new_should_assign_to_car
end
<p>This is better.  We can now pretty accurately determine that we are testing &#8216;get&#8217; on something called &#8216;new&#8217; and it should assign something.  If I mentioned that is was a <em>functional</em> test for a Ruby on Rails controller, you most likely would guess what I&#8217;m aiming for here.  One lesson that you might pick up here is that <strong>is</strong> 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.</p>


<p>So why don&#8217;t we take another stab at it?  This time, we&#8217;ll incorporate Shoulda, as I use this on a daily basis:</p>
1
2
3
4
5

context "get on new" do
  setup { get :new }
  should_assign_to :car
end
<p>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, &#8220;after doing a get on action new, an instance variable car should have something assigned&#8221;.  At first glance, this reads pretty easily, and for quite some while, I was stuck in this place.  It isn&#8217;t a bad place, but there is still room for improvement.  So, now I&#8217;m progressed my tests a bit further:</p>
1
2
3
4
5

context "requesting the new car form" do
  setup { get :new } 
  should_assign_to :car
end
<p>Notice the simple language change from what we are physically doing to what we are trying to accomplish.  These types of changes don&#8217;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&#8217;t let the quest for English like tests take you to far from what you are actually doing: writing tests.</p>


<p>We can now work on improving our vocabulary even further with another example.  Testing &#8216;create&#8217; actions for Rails controllers seems to be a straight forward set of tasks generally.</p>
1
2
3
4
5

context "creating a new car" do
  setup { post :create, :car => {:name => "My new Car"}}
  should_redirect_to "car_url(@car)"
end
<p>Finished?  Well not quite.  What happens if the car isn&#8217;t created correctly?  We need some more test code:</p>
1
2
3
4
5
6
7
8
9
10
11
12
13

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"
  end
end
<p>Now, we have a more complicated example.  Using nested contexts, we can group tests that are related together.  If you text editor or <span class="caps">IDE</span> has code folding capabilities, you only have to focus on what you are doing, and you still won&#8217;t lose site of your goal.</p>


<p>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 <span class="caps">REST</span> like manner.  We can also state the format of the reply we want just by manipulating the <span class="caps">URL</span>.  So, in a nutshell, this means if you exposing <span class="caps">HTML</span> along with <span class="caps">JSON</span> or <span class="caps">XML</span> or some other data format, you&#8217;ll need to test those as well.</p>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

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_stuff
      end
    end    
  end
  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 => ""}, :format => "xml"}
      should_respond_with :400
    end
  end
end
<p>We have lots of tests here.  Testing positive and negative conditions with multiple formats.  And notice that is no implementation here&#8230; so we did some <span class="caps">TDB</span> (test driven blogging) as well.  From here, only your imagination or impending deadlines can limit you.</p>


<p>So, what can we learn from all of this?  I&#8217;m sure there are multiple lessons buried somewhere in here, so I&#8217;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.</p>


<p>Thanks and <span class="caps">TATFT</span></p>

Posted in Uncategorized.