Caio Moritz Ronchi
2007-Nov-10 21:03 UTC
[rspec-users] Problem with view spec - works inside the browser but spec fails with nil object
Hi, I''ve been trying to find an answer for this problem in the last couple hours, but I think no discussion was about this exact same thing. So here it goes, hope someone can help. I''m trying to spec a view which works correctly on the browser, but that generates the following error when I run "rake spec:views". ActionView::TemplateError in ''/survey/show should display the "question 4" heading correctly'' You have a nil object when you didn''t expect it! The error occurred while evaluating nil.position On line #1 of app/views/survey/_question_for_candidate.rhtml 1: <div class="question question-<%= question.position %>"> 2: <p class="heading"> 3: <span class="number"><%= question.position %>.</span> 4: <span class="description"><%= question.description %></span> RSpec is telling me that the "question" object is nil. I can''t figure it out why. Here''s the spec that generates the error (the "it" block should test some tags inside the "response" object): require File.dirname(__FILE__) + ''/../../spec_helper'' describe ''/survey/show'' do fixtures ''questions'', ''alternatives'' before(:each) do assigns[:configurations] = {:survey_name => ''Whatever''} assigns[:questions] = Array.new assigns[:questions][4] = questions(:faixa_etaria) @faixa_etaria = questions(:faixa_etaria) end it ''should display the "question 4" heading correctly'' do render ''survey/show'' end end Here''s the :faixa_etaria Question fixture I''m using: faixa_etaria: id: 1 question_type_id: 1 description: ''Em qual das faixas et?rias abaixo voc? se inclui?'' position: 4 (The description value is in Portuguese). Here''s the "show" method inside the SurveyController: def show @configurations = {} Configuration.find(:all).each { |c| @configurations[c.name.to_sym] c.value } @questions = {} Question.find(:all).each { |q| @questions[q.position] = q } end Here''s the piece of code inside the "show.rhtml" template that''s calling the helper method that will cause that error: <%= render_question(@questions[4]) %> Here''s the "render_question" method implementation, inside the SurveyHelper class (invoking "render" seems to be the problem, but I don''t know why): def render_question(question) render(:partial => ''question_for_candidate'', :locals => {:question => question}) end The :locals Hash has :question as a key, and that is the variable that RSpec is complaining about inside the partial. Finally, here''s the "question_for_candidate" partial: <div class="question question-<%= question.position %>"> <p class="heading"> <span class="number"><%= question.position %>.</span> <span class="description"><%= question.description %></span> </p> <% for alternative in question.alternatives -%> <%= radio_button ''candidate'', "question_#{question.id}", alternative.id%> <label for="candidate_question"><%= alternative.description %></label> <br /> <% end -%> </div> I''m not that experienced with Rails nor RSpec, so I''m totally lost about this problem. -- Caio Moritz Ronchi -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/rspec-users/attachments/20071110/0b7523d1/attachment.html
Hans de Graaff
2007-Nov-11 08:01 UTC
[rspec-users] Problem with view spec - works inside the browser but spec fails with nil object
You are trying to test a lot of things at the same time, which is one of the reasons that it is now hard to diagnose a problem. I would tackle this by a) writing a separate spec for the partial _question_for_candidate b) write a separate spec for the helper that renders the question b) in the view spec for /survey/show use should_receive to determine that the helper is actually being called c) not use any fixtures in the view specs but instead use mocks for that: it''s faster and you can check exactly what needs to happen instead of relying on what happens to be in the fixture. I''m afraid that I don''t see immediately why your current setup won''t work, but untangling things along the lines sketched above should hopefully get you to a situation where it becomes easier to see what happens.> before(:each) do > assigns[:configurations] = {:survey_name => ''Whatever''} > assigns[:questions] = Array.new > assigns[:questions][4] = questions(:faixa_etaria)I am a little bit suspicious about this construction, though. I''m not sure whether assigns is a normal Array in this case, so I''d create the Array and populate it before handing it to assigns: @questions = Array.new @questions[4] = ... assigns[:questions] = @questions Kind regards, Hans -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: This is a digitally signed message part Url : http://rubyforge.org/pipermail/rspec-users/attachments/20071111/a479f059/attachment.bin
Caio Moritz Ronchi
2007-Nov-12 01:10 UTC
[rspec-users] Problem with view spec - works inside the browser but spec fails with nil object
Hi Hans, I rewrote my specs the way you suggested and now everything works! For those who are interested, here''s how the code looks like now (after following Hans'' tips).> a) writing a separate spec for the partial _question_for_candidaterequire File.dirname(__FILE__) + ''/../../spec_helper'' module QuestionForCandidatePartialHelper def get_question_type_mock(type) mock_model(QuestionType, {:name => type}) end def render_question_for_candidate_partial render(:partial => ''survey/question_for_candidate'', :locals => {:question => @question}) end end describe ''partial survey/_question_for_candidate.rhtml'' do include QuestionForCandidatePartialHelper before(:each) do @question = mock_model(Question, { :id => 1, :description => ''The description'', :position => 4, :alternatives => [mock_model(Alternative, {:id => 1, :description => ''Alternative 1 Description''})], }) @question_type_exclusive = get_question_type_mock(''exclusive'') @question_type_multiple = get_question_type_mock(''multiple'') end it ''should render the top of the template no matter the question type'' do @question.stub!(:question_type).and_return(@question_type_exclusive) render_question_for_candidate_partial response.should have_tag(''div.question.question-4'') do with_tag(''.heading .number'', ''4.'') with_tag(''.heading .description'', @question.description) end end it ''should render an exclusive-answer question'' do @question.stub!(:question_type).and_return(@question_type_exclusive) render_question_for_candidate_partial response.should have_tag(''div.question.question-4'') do with_tag(''input[type=?]'', ''radio'') without_tag(''input[type=?]'', ''checkbox'') end end it ''should render a multiple-answer question'' do @question.stub!(:question_type).and_return(@question_type_multiple) render_question_for_candidate_partial response.should have_tag(''div.question.question-4'') do with_tag(''input[type=?]'', ''checkbox'') without_tag(''input[type=?]'', ''radio'') end end end> b) write a separate spec for the helper that renders the questionrequire File.dirname(__FILE__) + ''/../spec_helper'' describe SurveyHelper do before(:each) do @question = mock_model(Question) end it ''should render a question using the question_for_candidate partial'' do self.should_receive(:render).with(:partial => ''question_for_candidate'', :locals => {:question => @question}) render_question(@question) end end> b) in the view spec for /survey/show use should_receive to determinethat the helper is actually being called require File.dirname(__FILE__) + ''/../../spec_helper'' describe ''/survey/show'' do before(:each) do assigns[:configurations] = {:survey_name => ''Whatever''} @positions = [4, (9..22).to_a].flatten @questions = Array.new @positions.each do |position| @questions[position] = mock_model(Question, { :id => 1, :description => ''The description'', :position => 4, :alternatives => [mock_model(Alternative, {:id => 1, :description => ''Description''})], :question_type => mock_model(QuestionType, {:name => ''exclusive''}) }) end assigns[:questions] = @questions end it "should render the ''survey/question_for_candidate'' partial" do args = {:partial => ''question_for_candidate'', :locals => {:question => an_instance_of(Question)}} template.should_receive(:render).with(args).exactly(@questions.nitems ).times render ''survey/show'' end end c) not use any fixtures in the view specs but instead use mocks for that: it''s faster and you can check exactly what needs to happen instead of relying on what happens to be in the fixture. As you can see mocks are now all over the place. No fixtures were used ;) Thanks a lot, Caio On Nov 11, 2007 6:01 AM, Hans de Graaff <hans at degraaff.org> wrote:> You are trying to test a lot of things at the same time, which is one of > the reasons that it is now hard to diagnose a problem. > > I would tackle this by > a) writing a separate spec for the partial _question_for_candidate > b) write a separate spec for the helper that renders the question > b) in the view spec for /survey/show use should_receive to determine > that the helper is actually being called > c) not use any fixtures in the view specs but instead use mocks for > that: it''s faster and you can check exactly what needs to happen instead > of relying on what happens to be in the fixture. > > I''m afraid that I don''t see immediately why your current setup won''t > work, but untangling things along the lines sketched above should > hopefully get you to a situation where it becomes easier to see what > happens. > > > before(:each) do > > assigns[:configurations] = {:survey_name => ''Whatever''} > > assigns[:questions] = Array.new > > assigns[:questions][4] = questions(:faixa_etaria) > > I am a little bit suspicious about this construction, though. I''m not > sure whether assigns is a normal Array in this case, so I''d create the > Array and populate it before handing it to assigns: > > @questions = Array.new > @questions[4] = ... > assigns[:questions] = @questions > > Kind regards, > > Hans > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-- Caio Moritz Ronchi -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/rspec-users/attachments/20071111/caa9da66/attachment.html