jfoutch
jfoutch

Reputation: 1398

Why are my Rspec tests failing?

I am writing a small Rails todo app for practice. I am trying to write my own Rspec tests for various things instead of just following a tutorial. I only have a few tests but a couple are failing and I can't figure out why. I try to make posting a question here a last resort but every time I have asked a question here I have gotten a good solution to my problem.

here's some code:

require 'spec_helper'

describe "TodosPages" do
  describe "home page" do
before { visit root_path }

it "should have the content 'Todo App'" do      
    page.should have_content('Todo App')
end  
it "should have title tag" do       
    page.should have_selector 'title'
end  
  it "should delete a todo and redirect to index" do    
      expect { click_link "Delete last todo" }.to change(Todo, :count).by(-1)
      response.should redirect_to :action => 'index'
  end
 describe "Add Todo" do
  subject { page }
   before {click_button "Add todo"}
   it { should have_selector('div.alert.alert-success', text: 'Todo Successfully Created') }

  end

  it "should have a error message" do
    pending
  end

  it "should create a Todo" do
   expect { click_button "Add todo" }.to change(Todo, :count).by(1)
  end


 end

end

 todos_controller



  class TodosController < ApplicationController
  def index
    @todo_items = Todo.all
    @new_todo = Todo.new
    render :index

  end

  def delete
    @todo_delete = Todo.last
    @todo_delete.delete
    redirect_to :action => 'index'

  end

   def add
   todo = Todo.create(:todo_item => params[:todo][:todo_item])
   unless todo.valid?
     flash[:error] = todo.errors.full_messages.join("<br>").html_safe
   else
     flash[:success] = "Todo Successfully Created"  
   end
  redirect_to :action => 'index'
end

def complete
    params[:todos_checkbox].each do |check|
       todo_id = check
       t = Todo.find_by_id(todo_id)
       t.update_attribute(:completed, true)
     end
     redirect_to :action => 'index'
    end
  end

   index.html.erb

<% @title = "Todo App" %>
<div class="container"> 
    <div class="row">
     <div class='span6' >

      <h1 class="hero-unit">Simple Todo App</h1>


      <p>All your todos here</p>
            <%= form_for @new_todo, :url => { :action => "add" }  do |f|  %>
            <%= f.text_field  :todo_item %>
            <%= f.submit "Add todo", class: "btn btn-primary" %>
         <%end%>

            <% if flash[:error] %>
             <div class="alert alert-error">
                <button type="button" class="close" data-dismiss="alert" >×</button>
                <strong><%= flash[:error] %></strong>
                </div>
            <% end %>

            <% if flash[:success] %>
           <div class="alert alert-success">
             <button type="button" class="close" data-dismiss="alert" >×</button>
                <strong><%= flash[:success] %></strong>
           </div>
      <% end %>

<div class="well">
    <%= form_tag("/todos/complete/", :method => "post") do %>
        <ul style="list-style-type:none;">
        <% @todo_items.each do |t| %> 
         <% if t.completed == true %>
         <li style="color:grey;"> <%= check_box_tag  "todos_checkbox[]",t.id %>  <strike><%= t.todo_item %></strike> </li>
      <% else %>
         <li> <%= check_box_tag  "todos_checkbox[]",t.id %> <%= t.todo_item %> </li>
      <% end %>
            <%end%>
            </ul>
                <%= submit_tag("Complete Todos", :class=>"btn btn-success") %>
            <%end %>
        </div> <!-- well --> 
        <%=  link_to "Delete last todo", delete_path %>

    </div> <!-- span6--> 
 </div> <!-- row --> 

  Failures:

 1) TodosPages home page should create a Todo
 Failure/Error: expect { click_button "Add todo" }.to change(Todo, :count).by(1)
   count should have been changed by 1, but was changed by 0
 # ./spec/views/index_page_spec.rb:29:in `block (3 levels) in <top (required)>'

 2) TodosPages home page should delete a todo and redirect to index
 Failure/Error: expect { click_link "Delete last todo" }.to change(Todo, :count).by(-1)
 NoMethodError:
   undefined method `delete' for nil:NilClass
 # ./app/controllers/todos_controller.rb:11:in `delete'
 # (eval):2:in `click_link'
 # ./spec/views/index_page_spec.rb:14:in `block (4 levels) in <top (required)>'
 # ./spec/views/index_page_spec.rb:14:in `block (3 levels) in <top (required)>'

 3) TodosPages home page Add Todo 
 Failure/Error: it { should have_selector('div.alert.alert-success', text: 'Todo Successfully   Created') }
   expected css "div.alert.alert-success" with text "Todo Successfully Created" to return something
 # ./spec/views/index_page_spec.rb:20:in `block (4 levels) in <top (required)>'

 Finished in 0.33114 seconds
 8 examples, 3 failures, 1 pending

 Failed examples:

 rspec ./spec/views/index_page_spec.rb:28 # TodosPages home page should create a Todo
 rspec ./spec/views/index_page_spec.rb:13 # TodosPages home page should delete a todo and redirect to index
 rspec ./spec/views/index_page_spec.rb:20 # TodosPages home page Add Todo 

 Randomized with seed 32260

Thanks in advance!

------Todo.rb


 class Todo < ActiveRecord::Base
    attr_accessible :todo_item, :completed
    validates :todo_item, presence: true , length: { maximum: 25 }

    end

Upvotes: 0

Views: 487

Answers (1)

simonb83
simonb83

Reputation: 108

I'll take a stab at this. In no particular order:

Test 2 (TodosPages home page should delete a todo and redirect to index)

It looks like your Todo table is empty in your test database, so when you call Todo.last in your controller action, you get nil and then try and call delete on nil.

I can't see anywhere in your test where you are creating at least one Todo object before the delete test.

On a separate note, in your controller you may want to consider using the destroy instead of the delete method.

Test 1 (TodosPages home page should create a Todo)

What do you have in your model for Todo in terms of validations? Are there any required fields? I'm guessing that :todo_item is a required field, but in your rspec test you just click on the button and don't provide any input for that field so its probably failing the model validation.

In your controller action for add, use create! instead of create, and that should give you additional error messages if it is failing model validations.

Test 3 (TodosPages home page Add Todo )

May be failing for the same reason your above test is failing, i.e. the validations failing. Fix that first and then see if this test is passing.

Hope this helps.

Edit: Examples of tests passing params

My personal preference would be to separate controller tests (testing specific controller functionality) from integration type test (making sure pages are populated properly, buttons appear and are clickable etc). For the latter I like to use Cucumber. Then you could write some much easier Controller tests using Rspec, which allow you to test specific logic as well as pass params directly to the action you are testing. A test would look like

describe "add Todo" do
 it "increases count by 1" do 
  expect {post: :add, todo_item: "string"}.to change(Todo, :count).by(1)
 end
end

Alternatively if you want to continue doing your integration test in Rspec, (assuming you are using Capybara for simulating the browser), then you would need to give your todo_item text field a unique id or name, and you could do:

it "should create a Todo" do
 fill_in "*todo_item_unique_id" with "some text"
 expect { click_button "Add todo" }.to change(Todo, :count).by(1)
end

Upvotes: 1

Related Questions