isolating controller tests from models
-
28-05-2021 - |
Question
Is it possible (and reasonable) to write Controller tests and classes before writing the underlying Model classes? I thought I saw notes on how to do that, but now I can't find the recipe.
For example, consider the following controller:
# file: app/controllers/premises_controller.rb
class PremisesController < ApplicationController
def create
@premise = Premise.new(params[:premise])
respond_with @premise
end
end
Can I test this controller code before creating the underlying Premise model and premises? I know the following won't work -- how would you re-write it (if it's possible)?
# file: spec/controller/premise_spec.rb
require "spec_helper.rb"
describe PremisesController do
context 'POST create' do
it 'should assign a new Premise to @premise' do
premise = stub_model(Premise)
Premise.stub(:create) { premise }
post :create
assigns(:premise).should == premise
end
end
end
end
update
The more I think about it, the more I'm convinced that I do need to define the Premise
class -- the PremisesController
code needs to reference it. So I'll change my question to "is it necessary to create the underlying premises
database table in order to run the PremisesController
tests?"
At this point, I don't see a good way around it (without changing the PremisesController
code, which defeats the point of testing). For example, the call to respond_with
calls @premise.has_errors?
which in turn accesses the database to fetch the column names. Unless I'm willing to stub methods internal to ActiveRecord
, I don't see how to avoid a hit to the DB.
But I'd love to be shown otherwise.
Solution 2
Okay - I've made my peace with this: it's not practical to create any meaningful tests unless the database table exists -- too many bits of ActiveRecord depend on having the table defined.
But this doesn't prevent writing test that with a clean separation between the controller and the model. This is eloquently covered by dchelimsky Himself in this RSpec issue. The gist of his post:
- Test using an integration test in spec/requests/premise_spec.rb which issues a
get
and verifies the generated json response. - With a skinny controller method (such as shown in the OP), don't bother writing a controller test.
- Write model tests to verify that the model is emitting the correct json.
By the way, I really recommend reading David's post -- he takes you through a step-by-step process, explaining the philosophy and the reasoning behind each step.
OTHER TIPS
I haven't tested this code yet, but pretty sure it should work. You could do this for now: Just a start, but you could try something like this if you want to, though it is probably bad practice to change your code like this just to pass specs.
class PremisesController < ApplicationController
def create
@premise = premise_until_model_finished params[:premise]
respond_with @premise
end
def premise_until_model_finished premise
Premise.new premise
end
end
require "spec_helper.rb"
describe PremisesController do
context 'when creating a premise' do
before :each do
@my_fake_model = {
:some_attribute => 'funk',
:some_other_attribute => 'a-delic'
}
PremisesController.any_instance.stub( :premise_until_model_finished).and_return(
@my_fake_model
)
PremisesController.any_instance.should_receive(
:premise_until_model_finished
).and_return( @my_fake_model )
post :create
end
it 'should create a premise as expected' do
# your criteria here...
end
end
end