Blog
An exploration of the art and
craft of software development
Testing controllers without gets/posts
Posted by Marty Haught on Saturday, October 18, 2008Another 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.