I have the following integration test. It loads a page with a form on it. The submit button is pressed without any data so the form should show an error box. The form is submitted with ajax and should put the form back on the page with the errors.
I can see this in the browser but the test fails.
What am I doing wrong? I am a complete NOOB so need some guidance.
require 'spec_helper'
require "rubygems"
describe "Boards" do
describe "board creation failure" do
attr_reader :selenium_driver
alias :page :selenium_driver
before(:all) do
#verification_errors = []
#selenium_driver = Selenium::Client::Driver.new \
:host => "localhost",
:port => 4444,
:browser => "*chrome",
:url => "http://localhost:3000/",
:timeout_in_second => 60
end
before(:each) do
#selenium_driver.start_new_browser_session
end
after(:each) do
#selenium_driver.close_current_browser_session
#verification_errors.should == []
end
it "should show the error explanation div" do
page.open "/"
page.click "board_submit"
page.is_element_present("error_explanation").should be_true #should show error box
end
end
I figured it out.
I needed to add the following metho to tell Selenium to wait until all of the ajax calls were completed.
I put this method in my spec/spec_helper.rb file and made sure to have require 'spec_helper' in the file.
Here is the method in spec_helper.rb:
#needed for selenium ajax testing
def wait_for_ajax(timeout=5000)
js_condition = 'selenium.browserbot.getUserWindow().jQuery.active == 0'
#selenium_driver.wait_for_condition(js_condition, timeout)
end
#needed for selenium ajax testing
def selenium_setup
#verification_errors = []
#selenium_driver = Selenium::Client::Driver.new \
:host => "localhost",
:port => 4444,
:browser => "*firefox",
:url => "http://localhost:3000/",
:timeout_in_second => 60
#return #selenium_driver
end
As you can see above I also moved the setup code for the selenium_driver to the spec_helper.rb file to clean up my code and make it more DRY:
Here are my integration tests file:
require 'spec_helper'
describe "Board form" do
attr_reader :selenium_driver
alias :page :selenium_driver
before(:all) do
selenium_setup
#selenium_driver.start_new_browser_session
end
after(:all) do
#selenium_driver.close_current_browser_session
#verification_errors.should == []
end
describe "create board" do
describe "failure" do
it "test_ home page form" do
page.open "/"
("Happy Birthday Greetings | Home").should == page.get_title
page.is_element_present("board_bp_name").should be_true
page.is_text_present("Name").should be_true
("Happy Birthday Greetings | Home").should == page.get_title
page.click "board_submit"
wait_for_ajax
page.is_element_present("board_bp_name").should be_true
page.is_text_present("Name").should be_true
page.is_element_present("board_birthday_1i").should be_true
page.is_element_present("board_submit").should be_true
page.is_text_present("exact:Oops, looks like 1 error occured: \n Hey whose birthday is it? Please enter a name.").should be_true
page.is_element_present("error_explanation").should be_true
end
end
describe "success" do
it "should create a new board for a properly filled in form and show the correct flash message" do
page.open "/"
page.type "board_bp_name", "Test User"
page.select "board_birthday_2i", "label=October"
page.select "board_birthday_1i", "label=1967"
page.select "board_birthday_3i", "label=7"
page.click "board_submit"
wait_for_ajax
page.wait_for_page_to_load("30000")
page.get_location().should =~ /boards\/\d/i
page.is_element_present("css=div.flash.success").should be_true
page.is_text_present("Your friend's board has been created.").should be_true
page.is_text_present("Test User").should be_true
page.is_text_present("43").should be_true
page.is_element_present("greeting_link").should be_true
page.is_text_present("Add greeting").should be_true
end
end
end
end
Related
I have a bit of a confusing rSpec issue - depending how I write my code, either the tests that describe the 'failing' specs fail or the tests that describe the 'successful' specs fail.
Here are the tests for the create action:
describe "POST 'create'" do
describe "failure" do
before(:each) do
#attr = {name: "", type_of_group: ""}
#student_attr = [{name: "Joe", gender: "Male"}, {name: "sally twotrees", gender: "Female"}]
#create = post :create, student_group: #attr, student: #student_attr
end
it "should have the right title" do
#create
response.should have_selector('title', :content => "Create a new group" )
end
it "should render the 'new' page" do
#create
response.should render_template('new')
end
it "should not create a user" do
lambda do
post :create, student_group: #attr
end.should_not change {#user.student_groups.count}
end
it "should flash an error message" do
#create
flash[:error].should =~ /please/i
end
end
describe "success" do
before(:each) do
#attr = FactoryGirl.attributes_for(:student_group)
# #student_attr = {name: "test", gender: "Male"}
end
it "should create a student_group" do
lambda do
post :create, student_group: #attr
end.should change {#user.student_groups.count}.by(1)
end
it "should create students" # do
# lambda do
# post :create, student_group: #attr, student: #student_attr
# end.should change {#student_groups.students.count}.by(1)
# end
it "should flash a success message" do
post :create, student_group: #attr
flash[:success].should =~ /has been added/i
end
it "should redirect" do
post :create, student_group_id: #group, student_group: #attr
response.should be_redirect
end
end
end
All of the 'failure' tests fail with this error:
Failure/Error: #create = post :create, student_group: #attr, student: #student_attr
ActionView::Template::Error:
`#student_group[students_attributes]' is not allowed as an instance variable name
if I write the code in my controller this way:
def create
#params = params[:student_group][:students_attributes]
#student_group = #user.student_groups.build(params[:student_group])
if #student_group.save
### RE: 'defensive coding' https://stackoverflow.com/questions/14502508/undefined-method-for-nilnilclass-when-pushing-values-to-an-array
if #params.present?
### https://stackoverflow.com/questions/11355820/rails-3-2-iterate-through-an-array
#params.each do |student|
#student_group.students.create(name:"#{student[:name]}", gender: "#{student[:gender]}")
end
end
# new subject path
redirect_to class_path(#student_group), flash: { success: "#{#student_group.name} has been added successfully" }
else
#title = "Create a new group"
flash.now[:error] = "Something's gone wrong. Please try again!"
render 'new'
end
end
and all of the 'success' tests fail if the controller code is written like this:
def create
#params = params[:student_group][:students_attributes]
#student_group = #user.student_groups.build(params[:student_group])
### http://railsforum.com/viewtopic.php?pid=40056#p40056
if #params.present?
#student = Student.new
else
#student = #student_group.students.build(#params)
end
if #student_group.save
### RE: 'defensive coding' https://stackoverflow.com/questions/14502508/undefined-method-for-nilnilclass-when-pushing-values-to-an-array
if #params.present?
### https://stackoverflow.com/questions/11355820/rails-3-2-iterate-through-an-array
#params.each do |student|
#student_group.students.create(name:"#{student[:name]}", gender: "#{student[:gender]}")
end
end
# new subject path
redirect_to class_path(#student_group), flash: { success: "#{#student_group.name} has been added successfully" }
else
#title = "Create a new group"
flash.now[:error] = "Something's gone wrong. Please try again!"
render 'new'
end
end
the form code is here: https://stackoverflow.com/a/17591802/2128691
from the above code it seems that your controller code is really messed up. In case of nested attributes, u just have to save the parent object. the child objects get saved automatically if they are valid. Also u dont need to assign the params the some instance object. they should be used directly. a simple example of nested attributes can be
User
has_many :comments
accepts_nested_attributes_for :comments
Comment
belongs_to :user
ur controller code should be as
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = 'success'
redirect_to some_path and return
end
render 'new'
end
the rspec controller test case can be as
it "should create a user with comments if valid data is provided" do
post :create, "user"=>{"name"=>"Prasad", "comments_attributes"=>{"0"=>{"comment"=>"first comment"}, "1"=>{"comment"=>"second comment"}}, "commit"=>"Save"
user = assigns[:user] #assigns lets u access the instance variable from the controller in the spec
user.should be_valid
user.comments.count.should == 2 #check that all the child models are saved
user.name.should == "Prasad"
user.comments.first.comment.should == 'first comment'
user.comments.last.comment.should == 'second comment'
response.should be_redirect(some_path) #since u redirected in the code
end
seriously, u need to go through rails guides.
I ended up using this code:
def create
#student_group = #user.student_groups.new(params[:student_group])
#params = params[:student_group][:students_attributes]
#student_group = #user.student_groups.build(params[:student_group])
if #student_group.save
### RE: 'defensive coding' http://stackoverflow.com/questions/14502508/undefined-method-for-nilnilclass-when-pushing-values-to-an-array
if #params.present?
### http://stackoverflow.com/questions/11355820/rails-3-2-iterate-through-an-array
#params.each do |student|
#student_group.students.create(name:"#{student[:name]}", gender: "#{student[:gender]}")
end
end
redirect_to new_student_group_subject_path(#student_group), flash: { success: "#{#student_group.name} has been added successfully. Next, add the subjects for this group" }
else
### http://railsforum.com/viewtopic.php?pid=40056#p40056
#student = #student_group.students.build
#title = "Create a new group"
flash.now[:error] = "Something's gone wrong. Please try again!"
render 'new'
end
end
I have setup Rspec2 + Spork + Guard + Devise
My files are as follows
#spec_helper.rb
Spork.prefork do
#code
Dir[Rails.root.join('spec/support/**/*.rb')].each {|f| require f}
RSpec.configure do |config|
config.extend ControllerMacros, :type => :controller
end
end
Spork.each_run do
# This code will be run each time you run your specs.
FactoryGirl.reload
include ControllerMacros
end
#spec/support/controller_macros.rb
module ControllerMacros
def login_user
before(:each) do
#request.env["devise.mapping"] = Devise.mapping[:user]
user = FactoryGirl.create(:user)
sign_in user
end
end
end
#spec/support/devise.rb
Spec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
end
in my request spec
#spec/features/documents_spec.rb
require 'spec_helper'
describe "Documents" do
login_user
describe "GET /documents" do
it "should display document name as sameera CV" do
#spec code
end
end
end
and when I run bundle exec guard, I get
1) Documents GET /documents should display document name as sameera CV
Failure/Error: Unable to find matching line from backtrace
NoMethodError:
undefined method `env' for nil:NilClass
# ./spec/support/controller_macros.rb:4:in `block in login_user'
So far I have done lots of fixes via google and nothing seems to be working, can someone help me :)
I'm on
rails 3.2.9
rspec 2.12.0
devise 2.2.3
any help would be greatly appreciated
Try changing #request.env["devise.mapping"] = Devise.mapping[:user] to request.env["devise.mapping"] = Devise.mapping[:user] in spec/support/controller_macros.rb
Here I'm answering my own question, and I was able to find a workaround for the question I asked.
Following are the steps I did
1) removed the controller_macros.rb and devise.rb from support directory
2) removed the ControllerMacros references from spec_helper.rb
3) Added the following code to
#spec/features/documents_spec.rb
before(:each) do
user = FactoryGirl.create(:user)
visit root_path
fill_in 'user_email', :with => user.email
fill_in 'user_password', :with => user.password
click_button 'Sign in'
end
I'm sure there should be a more elegant way (as describe in devise wiki), but this WORKS :)
I'm trying to follow Michael Hartl's Ruby on Rails Tutorial in http://ruby.railstutorial.org/chapters/sign-in-sign-out, but with some changes to practice, above all, some variations and the Test::Unit framework. In the tutorial, RSpec is used, while I'm trying to stick to Test::Unit + Shoulda-context.
In chapter 9 I'm suposed to pass some functional tests that use a var called 'controller', but my tests don't work as they find out that 'controller' doesn't exist. This is what I get:
marcel#pua:~/Desenvolupament/Rails3Examples/ror_tutorial$ rake
test:recent Loaded suite
/home/marcel/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.9.2.2/lib/rake/rake_test_loader
Started F
=============================================================================== Failure: test: POST 'create' with valid signin (email and password)
should redirect to the user show page. (SessionsControllerTest)
[test/functional/sessions_controller_test.rb:58]: Expected at least 1
element matching "title", found 0. is not true.
=============================================================================== E
=============================================================================== Error: test: POST 'create' with valid signin (email and password)
should sign in the user. (SessionsControllerTest): NameError:
undefined local variable or method `controller' for
test/functional/sessions_controller_test.rb:53:in `block (3 levels) in <class:SessionsControllerTest>'
=============================================================================== Finished in 0.957865676 seconds. 7 tests, 6 assertions, 1 failures, 1
errors, 0 pendings, 0 omissions, 0 notifications 0% passed
7.31 tests/s, 6.26 assertions/s rake aborted! Command failed with status (1): [/home/marcel/.rvm/rubies/ruby-1.9.2-p290/b...] Tasks: TOP
=> test:recent (See full trace by running task with --trace)
This is the original (RSpec) test:
describe SessionsController do
...
describe "POST 'create'" do
...
describe "with valid email and password" do
before(:each) do
#user = Factory(:user)
#attr = { :email => #user.email, :password => #user.password }
end
it "should sign the user in" do
post :create, :session => #attr
controller.current_user.should == #user
controller.should be_signed_in
end
it "should redirect to the user show page" do
post :create, :session => #attr
response.should redirect_to(user_path(#user))
end
end
end
end
and this is my translated (into Test::Unit + Sholuda-context) test:
class SessionsControllerTest < ActionController::TestCase
context "POST 'create'" do
context "with valid signin (email and password)" do
setup do
#attr = {email: "test#email.tst", password: "testpwd"}
#user=User.create! #attr.merge!({name: "test_user", password_confirmation: "testpwd"})
end
should "sign in the user" do
post :create, :session => #attr
assert_equal #user, controller.current_user
end
should "redirect to the user show page" do
post :create, :session => #attr
assert_select "title", /Show/
end
end
end
end
Has anybody any idea how to make my test work?
Looking at the official Rails testing guide at http://guides.rubyonrails.org/testing.html, I've seen that an instance variable called #controller is enabled in functional tests. so, the Test::Unit version should be:
should "sign in the user" do
post :create, :session => #attr
assert_equal #user, #controller.current_user
end
I'm trying to make a simple app. When Im testing it in browser everytyhing works just fine. Howerver, when I try to run some tests with RSpec (2.5) it fails when it comes to :create test for controller.
Here's my create method:
def create
#website = Website.new(params[:website])
if #website.save
flash[:notice] = "Website created."
redirect_to(:action => 'list')
else
render('new')
end
end
The controller test:
describe WebsitesController do
render_views
.
.
.
describe "POST 'create'" do
before(:each) do
#attr = { :adres => "www.excc.pl", :opis => "aaa "*22, :tagi => "aaa aaa aaa",
:preview => File.new(Rails.root + 'spec/fixtures/rails.png'),
:preview_mini => File.new(Rails.root + 'spec/fixtures/rails.png')}
end
describe "success" do
it "should have the right title" do
response.should have_selector("title", :content=>"Lista witryn w portfolio")
end
end
.
.
.
The result of this test:
1) WebsitesController POST 'create' should have the right title
Failure/Error: response.should have_selector("title", :content=>"Lista witryn w portfolio")
expected following output to contain a <title>Lista witryn w portfolio</title> tag:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
# ./spec/controllers/websites_controller_spec.rb:34:in `block (4 levels) in
websites_controller_spec.rb:34 refers to create method
However, this test is passed correctly (for incorrect data it should be redirected back to 'new' site with specified title):
it "should have the right title" do
post :create, :website => #attr.merge(:adres => "")
response.should have_selector("title", :content=>"Dodaj stronÄ™ WWW")
end
The second problem is...
There was a time when I've got a test result like this:
<html><body>You are being redire cted.</body></html>
... which was causing me to pull my hair out for some time until I've done sth (I don't really know what) and it was gone. Yet, it makes me scared like hell when I think that it can come back in future an ruin my happiness.
Any thoughts on this would be greatly appreciated.
It's hard to know what is being asked here, but I believe the issue is that you are not setting the conditions for success/failure. If I understand correctly, when you pass in an blank :adres attribute, the save should fail and the page should render the list action. So you want to stub the create method and return true or false depending on the expected result:
it "succeeds" do
#website = mock_model(Website,:save=>true)
Website.stub(:new) { #website }
post :create, :website => {}
# redirects
response.should have_selector("etc etc")
end
it "fails" do
#website = mock_model(Website,:save=>false)
Website.stub(:new) { #website }
post :create, :website => {}
# renders 'new'
response.should_not have_selector("etc etc")
end
Testing of the validity of the parameters should be performed in the model spec:
#website = Website.new(:adres=>"")
#website.should_not be_valid
Working through Michael Hartl's RailsTutorial and came across the following error - even though I have followed everything to the 'T'.
1) UsersController GET 'index' for signed-in users should have an element for each user
Failure/Error: response.should have_selector("li", :content => user.name)
undefined method `name' for #<Array:0x000001032c07c8>
Did anyone else get a similar error and know how to fix it?
I am in Chapter 10.
Btw, when I try the page it does what it is supposed to do. It's just that the test fails in RSpec.
FYI, here is the related test code from the users_controller_spec.rb
require 'spec_helper'
describe UsersController do
render_views
describe "GET 'index'" do
describe "for non-signed-in users" do
it "should deny access" do
get :index
response.should redirect_to(signin_path)
flash[:notice].should =~ /sign in/i
end
end
describe "for signed-in users" do
before(:each) do
#user = test_sign_in(Factory(:user))
second = Factory(:user, :email => "another#example.com")
third = Factory(:user, :email => "another#example.net")
#users = [#user, second, third]
end
it "should be successful" do
get :index
response.should be_success
end
it "should have the right title" do
get :index
response.should have_selector("title", :content => "All users")
end
it "should have an element for each user" do
get :index
#users.each do |user|
response.should have_selector("li", :content => user.name)
end
end
end
end
My spec/spec_helper.rb file looks like the following:
require 'rubygems'
require 'spork'
Spork.prefork do
# Loading more in this block will cause your tests to run faster. However,
# if you change any configuration or code from libraries loaded here, you'll
# need to restart spork for it take effect.
ENV["RAILS_ENV"] ||= 'test'
unless defined?(Rails)
require File.dirname(__FILE__) + "/../config/environment"
end
require 'rspec/rails'
# Requires supporting files with custom matchers and macros, etc,
# in ./support/ and its subdirectories.
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
Rspec.configure do |config|
# == Mock Framework
#
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
#
# config.mock_with :mocha
# config.mock_with :flexmock
# config.mock_with :rr
config.mock_with :rspec
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, comment the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
### Part of a Spork hack. See http://bit.ly/arY19y
# Emulate initializer set_clear_dependencies_hook in
# railties/lib/rails/application/bootstrap.rb
ActiveSupport::Dependencies.clear
def test_sign_in(user)
controller.sign_in(user)
end
def integration_sign_in(user)
visit signin_path
fill_in :email, :with => user.email
fill_in :password, :with => user.password
click_button
end
end
end
Spork.each_run do
end
it appears your test_sign_in method is returning an instance of an array rather than a User object. Are you explicitly returning a user object in the test_sign_in method? If not, have a look at the last line that's executed in that method, I have a feeling the result of it is an array.
I solved this issue, and the answer can be found on the railstutorial official forums.