Blog An exploration of the art and
craft of software development

Testing controllers without gets/posts

Posted by Marty Haught on Saturday, October 18, 2008

Another conversation that has come up recently was how best to test protected methods in controllers directly. You might ask why would you care but this is fairly important to me. I like to test in isolation especially for ‘helper’ methods in controllers, or ApplicationController itself. When you isolate just the method you can test all its permeations more directly.

In Rspec this is fairly easy but doesn’t seem to be commonly known. Rspec gives us the controller object to interact with. With it we can interact directly with the controller instance and do not need to call get or post. I’ll give two examples to demonstrate the usefulness of this approach.

First, I have a method in ApplicationController that is usually invoked via before_filter. However, I really want to test it as it contains business logic. Here is the spec example:


describe ApplicationController, ".default_object" do	
	it "should invoke AppObject.find_default" do
		@default_object = mock('DefaultObject')
		AppObject.should_receive(:find_default).and_return(@default_object)
		controller.default_object.should == @default_object	  
	end
end

As I start off my describe block with ApplicationController as the class it will set it as controller. In this spec I am testing one aspect of the default_object method. I go ahead create a mock to test the return and mock up the method on some Class that the default_object method will use. The final bit is to call the method on controller just like you would for a model spec.

The second example is a bit more tricky as it involved a protected (or private) method on a controller that you wish to test.


describe MyController, ".secret_helper" do	
	it "should set the secret key as @secret_key on the controller" do
		controller.instance_variable_get(:@secret_key).should be_nil
		controller.send(:secret_helper, "my_key")
		controller.instance_variable_get(:@secret_key).should == "my_key"
	end
end

Here I use typical Ruby methods to do my testing. First, I want to insure that there’s no instance variable set for @secret_key. Next, I use send to invoke the protected method, which you cannot call with just a controller.secret_helper. Finally I test that the instance variable was set to what I expected.

blog comments powered by Disqus