Odd Tests: Classical vs. Mockist & State Vs. Behavior Testing
I’m continuing to build out my Sinatra app with more and more features which means that my main app file is continuing to gain more routes and controller actions. In a previous post I discussed a sinatra controller-type test and I’ve found myself using a similar structure to test the new actions I’m adding in. However, these test still feel odd (and maybe not quite right?) to me so I’m going to type through them one more time framed by Martin Fowler’s excellent article Mocks Aren’t Stubs.
In a typical Rails application you have a route like this:
get "accounts/show/:id", :controller => 'accounts', :action => 'show'
and then a controller action that looks like this:
class AccountsController < ApplicationController
def show
@account = Account.find(params[:id)
end
end
To accomplish the same thing in Sinatra you can put your route and your action in the app file:
class Bank < Sinatra::Application
get "/accounts/show/:id" do
@account = Account.find(params[:id])
erb '/accounts/show'.to_sym
end
When I test these actions like I did in my pretious post I’m using what Fowler refers to as the ‘Mockist’ style of testing, and I’m testing behavior rather than state. The test for this action would something like this:
it "should ask the database for the account and render the show page" do
account = mock(Account)
Account.should_receive(:find).and_return(account)
get "/accounts/show/1"
last_response.should be_ok
end
What this test does is tests the behavior of the actions, so that when I call the GET "/accounts/show/:id"
action I test that the Accounts class should_receive the find
method, and then I’m stubbing out what that find
method returns so that it returns a mock Account object I created in the first line of the test setup. The final line of the test is just testing that the view rendered properly, which is more of a state-based test.
I guess this test is ok and the failing messages provide some guidance to write the production code, but I think I’m definitely more comfortable with the 'Classical’ approach, which uses objects to confirm the expected state after an action is executed. Testing state feels much more finite and is how I choose to test almost everything outside of these controller actions. Even if I use mock objects I’m still testing that something in the state of my mock objects changed after I used them. Again, it just feels much more concrete than something like the should_receive
method.
However, I’m honestly not sure of how to Classicly test some of these Sinatra actions so for now I’ll stick to the Mockist style. RSpec offers some helpers for testing the variables established in Rails controllers, but no such luck in Sinatra. If anyone has any tips please hit me up over on Twitter. Thanks!