Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rspec and Rails with View Tests and Nested Resources

I'm working on a Yahoo Answers sort of app to improve my Rails skills. So far I've set two models "Question" and "Answers" and they are nested this way:

  resources :questions do
    resources :answers
  end

I've made tests for the controllers, models and the questions' views, but I'm having a little trouble with the answers' view and the nested routes. I'm using Rspec and Factory girl.

I have the following test:

describe "answers/new.html.erb" do
  before(:each) do
    @question = Factory(:valid_question)
    @answer = Factory(:valid_answer)
    assign(:question, @question)
    assign(:answer, stub_model(Answer,
      :text => "MyString",
      :question_id => 1
    ).as_new_record)
  end

  it "renders new answer form" do
    render
    assert_select "form", :action => question_answers_path(@question), :method => "post" do
      assert_select "textarea#answer_text", :name => "answer[text]"
      assert_select "input#answer_question_id", :name => "answer[question_id]"
    end
  end
end

and whenever I run the test I get the following message:

  3) answers/new.html.erb renders new answer form
     Failure/Error: render
     ActionView::Template::Error:
       No route matches {:controller=>"answers"}
     # ./app/views/answers/new.html.erb:6:in `_app_views_answers_new_html_erb__3175854877830910784_6513500'
     # ./spec/views/answers/new.html.erb_spec.rb:16:in `block (2 levels) in <top (required)>'

I've tried many things like doing

render new_question_answer_path(@question)

but I get this:

  3) answers/new.html.erb renders new answer form
     Failure/Error: render new_question_answer_path(@question.id)#, :format=>:html
     ActionView::MissingTemplate:
       Missing partial /questions/1/answers/new with {:handlers=>[:erb, :builder, :coffee], :formats=>[:html, :text, :js, :css, :ics, :csv, :xml, :rss, :atom, :yaml, :multipart_form, :
url_encoded_form, :json], :locale=>[:en, :en]}. Searched in:
         * "/home/juan/rails_projects/answers/app/views"
     # ./spec/views/answers/new.html.erb_spec.rb:16:in `block (2 levels) in <top (required)>'

Would you please help me with this? I'm kind of clueless right now.

like image 285
Figarillo Avatar asked Jul 08 '12 00:07

Figarillo


2 Answers

I think the error is in your view. Can you add it?

Also, here's some advice on using RSpec:

  • You could put both @question and @answer in a let block. It's the preferred way of doing it these days. Check out the docs, it's fairly straightforward to use.
  • You should actually use FactoryGirl.create, not Factory(). You can shorten it to create if you include Factory::Syntax::Methods in your RSpec configuration.
  • It's generally not a good idea to mix test doubles with real models. You should either isolate the views from the models or integrate them all the way - either replace the stub_model with Answer.build or use a stub for @question and @answer. FactoryGirl has Factory.build_stubbed that is basically stub_model appropriate for view specs.
  • View specs have fallen out of grace. I suggest searching the RSpec mailing list for details on why people choose to avoid them. My take on it is that they are fairly fragile (easy to break when changing code), because of their dependencies on models and helpers. Either they force you to stub a lot, integrate the models, or write a simple presenter. That being said, they have their uses, but they are rare. A better alternative would be to test this interaction in integration, either using cucumber, steak or just rspec and capybara.
  • Your assertions are examples of things you usually don't want to test in a view spec. You're asserting the presence of some markup, including form fields, which is not a good test per-se, since it tells you that the form is there, but not that it is working. You'll get a better coverage in integration. Also, it will be less fragile - if you rename the model or the field, for example, you won't need to change the view spec.
like image 102
Stefan Kanev Avatar answered Nov 03 '22 21:11

Stefan Kanev


I ran into this issue. If you look at the stack trace closely, you'll see that your view is getting called correctly, but there's an error on line 6.

In my case this case, this was caused by a call to one of the rails path helps, something like answers_path(@question), but it was getting passed nil.

The fix is to add an assign call for that instance variable. If a local variable is used instead, then it can be passed in via the :locals hash when calling render.

like image 38
M. Scott Ford Avatar answered Nov 03 '22 19:11

M. Scott Ford