Mark Needham

Thoughts on Software Development

Archive for the ‘rspec’ tag

RSpec: Testing Rails routes

without comments

Something which I keep forgetting is how to write controller tests where I want to check whether an action correctly redirected to another action.

With most of the routes in our application we’ve created a ‘resourceful route’ where each action maps to a CRUD operation in the database.

We can do that with this type of code in routes.rb:

ActionController::Routing::Routes.draw do |map|
	map.resources :foos
end

Several helper methods based on named rotes get created and included in our controllers when we do this and we have access to those inside our specs.

We can see the named routes by executing the following command from the terminal:

mneedham@markneedham.local ~/SandBox/rails_test$ rake routes CONTROLLER=foos
(in /Users/mneedham/SandBox)
    foos GET    /foos(.:format)          {:action=>"index", :controller=>"foos"}
         POST   /foos(.:format)          {:action=>"create", :controller=>"foos"}
 new_foo GET    /foos/new(.:format)      {:action=>"new", :controller=>"foos"}
edit_foo GET    /foos/:id/edit(.:format) {:action=>"edit", :controller=>"foos"}
     foo GET    /foos/:id(.:format)      {:action=>"show", :controller=>"foos"}
         PUT    /foos/:id(.:format)      {:action=>"update", :controller=>"foos"}
         DELETE /foos/:id(.:format)      {:action=>"destroy", :controller=>"foos"}

The following helper methods would be created for this resource as per the documentation in resources.rb:

# Named Route Helpers
# ============ =====================================================
# foos foos_url, hash_for_foos_url,
# foos_path, hash_for_foos_path
#
# foo foo_url(id), hash_for_foo_url(id),
# foo_path(id), hash_for_foo_path(id)
#
# new_foo new_foo_url, hash_for_new_foo_url,
# new_foo_path, hash_for_new_foo_path
#
# edit_foo edit_foo_url(id), hash_for_edit_foo_url(id),
# edit_foo_path(id), hash_for_edit_foo_path(id)

Keeping this in mind means that if we do something like this inside our ‘create’ action:

class FoosController < ApplicationController
   def create
      ...
      redirect_to :action => :index
   end
end

We’d be able to test that redirection with a spec along the following lines:

describe "POST create" do
   it "should redirect back to the index page" do
      post :create, :foo => { :bar => "value" }
 
      response.should redirect_to foos_path
   end
end

We can also use any of those other helper methods inside our tests which seems obvious looking at it now but wasn’t for me until a colleague pointed it out.

It is pretty cool to be able to write specs like this and they seem much more readable than the equivalents you’d be able to test around ASP.NET MVC code in C#.

Written by Mark Needham

October 13th, 2010 at 6:25 pm

Posted in Ruby

Tagged with ,

RSpec: Fooled by stub!…with

with one comment

We had an RSpec spec setup roughly like this the other day…

describe "my stub test" do
  it "should be amazin" do
    Mark.stub!(:random).with("some_wrong_argument").and_return("something")
 
    Another.new.a_method
  end
end

…where ‘Mark’ and ‘Another’ were defined like so:

class Mark
  def self.random(params)
    "do some amazing stuff"
  end
end
class Another
  def a_method
    random = Mark.random("foo")
    # use random for something
  end
end

When we ran the spec we would get the following error message which was initially a little baffling:

NoMethodError in 'my stub test should be amazin'
undefined method `random' for Mark:Class
./rspec_spec.rb:9:in `a_method'
./rspec_spec.rb:17:

We spent quite a while looking at how we’d set up the spec to make sure we were actually calling ‘stub!’ on the right class before eventually realising that we had unintentionally added ‘with(“some_wrong_argument”)’ to that stub.

A little investigation of the RSpec code shows that this is the expected behaviour for this scenario:

module Spec
  module Mocks
    class Proxy
      ...
      def message_received(sym, *args, &block)
        expectation = find_matching_expectation(sym, *args)
        stub = find_matching_method_stub(sym, *args)
 
        if ok_to_invoke_stub?(stub, expectation)
          record_stub(stub, sym, args, &block)
        elsif expectation
          invoke_expectation(expectation, *args, &block)
        elsif expectation = find_almost_matching_expectation(sym, *args)
          record_almost_matching_expectation(expectation, sym, *args, &block)
        else
          @target.__send__ :method_missing, sym, *args, &block
        end
      end
    end
  end
end

In this case ‘find_matching_method_stub’ returns a nil value and since we didn’t set up any expectation on ‘Mark’ we fall through to the ‘method_missing’ exception.

Written by Mark Needham

September 26th, 2010 at 7:03 pm

Posted in Ruby

Tagged with ,

RSpec: Causing ourselves much pain through ‘attr’ misuse

with 2 comments

While testing some code that we were mixing into one of our controllers we made what I thought was an interesting mistake.

The module we wanted to test had some code a bit like this…

module OurModule
  def some_method
    @User = User.find(params[:id])
 
    # in the test code this is always true
    if @user == user
      ...
    end
  end
end

..and we had the spec setup like so:

describe 'OurController' do
  class TestController
    include OurModule
    attr_accessor :user
  end
 
  before(:each) do
    @controller = TestController.new
    @controller.user = Factory(:user)
  end	
 
  it "should do something" do
    # whole load of setup not related to our mistake
    @controller.some_method
  end
end

We created the ‘attr_accessor’ so that we would be able to choose a value for ‘user’ to simulate the fact that Warden mixes in a ‘user’ method into our ApplicationController at runtime.

The problem we ran into was that both ‘user’ and ‘@user’ inside ‘some_method’ were always returning the same value even though we set ‘user’ to be something else in our spec before each spec is run.

We eventually rotated pairs and immediately realised that the reason that was happening was because ‘user’ was in fact referring to the value we’d just set for ‘@user’ since in the test ‘user’ is created by an ‘attr_accessor’ call.

As it turns out Warden also mixes in a ‘current_user’ method so we changed the code to make use of that instead of ‘user’ since that was the team’s agreed standard and the call to ‘user’ hadn’t yet been replaced.

It does show an interesting side effect of setting instance variables in modules and also suggested that we had setup the test wrong to being with.

A better approach would have been to simulate what the actual code would do more closely and define a ‘user’ method which returned a canned user in a way that only relied on code in the test and wouldn’t change based on what the system under test did.

Written by Mark Needham

September 26th, 2010 at 6:57 pm

Posted in Ruby

Tagged with ,

rspec – Invalid character ‘\240′ in expression

with 4 comments

We have been using rspec on my project for the unit testing of our Ruby code and while running one of the specs last week I ended up getting this somewhat en-cryptic error message:

Invalid character '\240' in expression
...

After convincing myself that this error wasn’t actually possible it turned out that I had somehow entered an ‘invisible to TextMate’ character after one of the method definitions – on the editor it just looked like a space.

I’m not sure exactly what the character was but deleting it got rid of the error and got my test/spec running again.

Written by Mark Needham

October 6th, 2008 at 8:48 pm

Posted in Ruby

Tagged with ,