Capybara can't find link on the page - ruby-on-rails-3

I'm trying to get my first capybara tests going. I following Ryan Bate's philosophy and putting some functional tests into my controller spec files.
describe UsersController do
render_views
it "can get the home page" do
get 'home'
response.body.should include("Login")
end
it "should log in" do
get 'home'
puts response.body
click_link('Login')
response.body.should include("Email")
response.body.should include("Password")
end
end
In it should login I ran into problems so began with just trying to make sure I can find and click the link. No luck. In the test as above I'm just trying to make sure the link exists
The puts response.body produces the following output
...
<div id="user_nav">
Register or Login
</div>
...
and I also see the element on the actual page. It seems only my test can't find it. The first test does pass.
1) UsersController should log in
Failure/Error: click_link('Login')
Capybara::ElementNotFound:
no link with title, id or text 'Login' found
# (eval):2:in `click_link'
# ./spec/controllers/users_controller_spec.rb:14:in `block (2 levels) in <top (required)>'
I'm using Rails 3.2, Rpsec 2.11 and Capybara 1.1.2.
(I've already checked the other questions on stackoverflow as well as a few tutorials and screencasts. I can't see any reason it can't find an element given an id tag, but I'm probably missing something obvious.

I think the reason it's not working is that you're using get for what is essentially an integration test. See this post: http://blog.plataformatec.com.br/2012/06/improving-the-integration-between-capybara-and-rspec/
If my understanding is correct, you need to use visit in order to use click_link on the page:
it "should log in" do
visit home_path
click_link('Login')
page.should ...
end
See also this answer on SO: Rspec and capybara, difference between visit and get methods, with regards to the current_path object

Related

single test failing after changing to capybara-webkit

I have written some RSpec test for my rails 3.2 application and because I was annyoed by the Browser popping up ich tried to change from firefox to capybara-webkit.
After this all tests still run, except one. The line that is failing is:
expect { click_button "Create" }.to change(Answer, :count).by(count)
If I remove the expect and add a method to take a screenshot before and after, I can see that the test is run correctly. But if I step trough with the Debugger the log shows me that the records get created after the second screenshot line. I can wait forever the click_button and corresponding Controller action is run after the line next line is executed.
The "create" button is a standard html button, no JS is involved in the create action. Does sb have a explaination for this strange behaviour?
There is a race condition here between Capybara sending the click action to the server and your test checking the database.
The simplest way to resolve this is to wait before checking:
expect { click_button "Create"; sleep 2 }.to change(Answer, :count).by(count)
I don't like this. A better way to test this would be to check from the end user's perspective.
For example, after clicking 'Create', does the user see the answer on the answers page?
fill_in :title, :with => "My answer"
click_button 'Create'
page.should have_text "My answer"

Rails 3 RSpec and assert_select

I've thought to try to use Rspec. But I get a next problem with the assert_select.
1) UserController login page open login page contains all expected controls
Failure/Error: assert_select "form[action=?]", "/user/login" do MiniTest::Assertion:
Expected at least 1 element matching "form[action='/user/login']", found 0.
# (eval):2:in `assert'
# ./spec/controllers/user_controller_spec.rb:20:in `block (3 levels) in <top (required)>'
This is my code snippet
describe UserController do
describe "login page open" do
it "login page contains all expected controls" do
get :login
assert_select "form[action=?]", "/user/login" do
assert_select "input[name=?]", "username"
assert_select "input[name=?]", "password"
assert_select "input[type=?]", "submit"
end
end
end
When I open a login page in a browser this page opens without problem.
By default, RSpec (at least in newer versions) prevents Rails from rendering views when you run controller specs specs. They want you to test your views in view specs, not controller specs. Since the views don't render, assert_select always fails.
But for people who (like me) want to test the occasional snippet of a view in their controller specs, they provide a render_views method. You have to call it in your describe or context block, though, not inside the it block.
describe UserController do
render_views # <== ADD THIS
describe "login page open" do
it "login page contains all expected controls" do
get :login
assert_select "form[action=?]", "/user/login" do
assert_select "input[name=?]", "username"
assert_select "input[name=?]", "password"
assert_select "input[type=?]", "submit"
end
end
end
Controller tests are for testing controllers.
assert_select matches something that is in your view code.
It is a good idea keep your controllers separated from your views, and this includes tests done on controllers and on views. You should use assert_select in your views test (the ones that are usually on spec/views), not on your controller tests.

Rails 3 assert_select undefined method matches?

Trying to get started on view testing in Rails 3. I want to validate that I have a form getting kicked out in the view that has the right URL for the action. So I am using assert_select. I actually got a failing test first, using this syntax in the spec (using Rspec):
response.should assert_select "form[action=#{my_model_path}]"
Looking at the rendered HTML, sure enough, the view was rendering the 'edit' url, not the 'new' url due to the wrong model being passed down. Groovy, start red.
I make the model a 'new' one, and I look at my rendered output, and it's what I'd expect, BUT the test fails, and the error message says:
NoMethodError:
undefined method `matches?' for #<Array:0x0000012a1a5d58>
I've looked all over the web for this, found one guy that mentioned the error, but got no resolution. Any ideas?
Figured out that I could get this to work by nesting my assert_select calls, like so:
response.should assert_select "form" do
assert_select "[action=?]", my_model_path
end

How to submit the rails3.1 engine form rendered inside the main app to be submitted to the engine's controller?

I'm building a new rails3.1 engine for commentable functionality.
I created the engine, generated the resource named Comment.
The engine's config/routes.rb has:
Kurakani::Engine.routes.draw do
resources :comments
end
The spec/dummy rails app has a resource named Post and its routes has:
Rails.application.routes.draw do
resources :posts do
resources :comments
end
mount Kurakani::Engine => "/kurakani"
end
I've setup the associations between the engine's Comment model and dummy rails app's Post model.
Then inside the spec/dummy rails app, I've rendered out the comment form in the show template of the Post.
The form also gets generated with its action path to post/1/comments.
When I run the spec, I think it tries to search for the controller inside the spec/dummy app itself instead of submitting to the engine's app/controllers/kurakani/comments_controller.rb, so I get the following error when I run the spec.
$ bundle exec rspec spec/integration/comments_spec.rb ruby-1.9.2-p180
No examples matched {:focus=>true}. Running all.
Run filtered excluding {:exclude=>true}
/Users/millisami/gitcodes/kurakani/app/views/kurakani/comments/_form.html.erb:3:in `___sers_millisami_gitcodes_kurakani_app_views_kurakani_comments__form_html_erb___1787449207373257052_2189921740'
F
Failures:
1) Kuraki::Comment authenticated user creating a new comment
Failure/Error: click_button "Create"
ActionController::RoutingError:
uninitialized constant CommentsController
# ./spec/integration/comments_spec.rb:29:in `block (3 levels) in <top (required)>'
How can I specify the comment to be submitted to the engine's comments_controller.rb instead of the spec/dummy app??
If I couldn't make the problem clear, I've pushed the repo at https://github.com/millisami/kurakani
The problem is that your route for the form that is generated goes to /posts/:post_id/comments, which is the route defined in your application, but not your engine. Your engine only defines this route:
resources :comments
This is (almost) working because the engine sees that the application has the route which matches to CommentsController, it's just that there's no CommentsController for it to go to.
I downloaded the application from GitHub and had a play around and changed the form_for in app/views/kurakani/comments/_form.html.erb to be this:
form_for(Kurakani::Comment.new, :url => kurakani.comments_path)
This makes the test pass, but I am not sure if it actually gives you what you want. You're probably going to want to play around with that URL part yourself.
What's happening here is that this view is rendered by the main application using the kurakani_list helper in spec/dummy/app/posts/show.html.erb, meaning that any URL helper that you reference (directly or indirectly) will point to the application and not to the engine, like I think you want it to.
So in order to tell Rails what the true route is that we want our form to go to we must specify the :url option and tell it we want to go to kurakani.comments_path, rather than the comments_path which may be defined by the application.
If we wanted to do the reverse (reference an application route from within an engine), we would use main_app rather than kurakani.

Rspec view testing with capybara and rails3

I really like the way RSpec is able to separate controller and view tests but have some problems with getting capybara matchers to work in a view test. What i basically try to achieve is sth like this:
describe "some page" do
it "should render with lots of stuff" do
assign ..
render
rendered.should have_button ('Any button') #or any capybara matcher, really
end
end
I've seen some posts on the net showing how to configure capybara and rails3 to work smoothly with cucumber or rspec controller tests, but this is not really what I want - that is, testing the views at the lowest level possible.
Also if there's another way to do this (not requiring lots of custom code, couse I know i could write some matchers that extract given selectors from rendered using nokogiri or whatever tool suitable) that'd be great too - using capybara is not a requirement.
There is now an option to use Capybara matchers (without Webrat baggage) when testing controllers (and views too). I'm using it this way:
describe GlobalizeTranslationsController do
render_views
let(:page) { Capybara::Node::Simple.new(#response.body) }
describe "PUT :update" do
before do
put :update
end
it "displays a flash notice" do
page.should have_selector('p.notice')
end
end
end
Full code:
https://github.com/Exvo/exvo_globalize/blob/master/spec/controllers/globalize_translations_controller_spec.rb
References:
http://robots.thoughtbot.com/post/8087279685/use-capybara-on-any-html-fragment-or-page
http://cakebaker.42dh.com/2010/09/19/cucumber-switching-from-webrat-to-capybara/
http://trevorturk.com/2010/12/22/using-capybara-without-cucumber-in-rails-3
Capybara currently does not work with view specs (there are plans to make it work in the future). The simplest answer is to just add gem 'webrat' to the Gemfile and you're basically set. You might not have have_button but you'll have have_selector, have_tag and similar available.
Btw: as far as I know capybara and webrat can co-exist in one project.
Slightly simpler than Pawel's answer, but the gist is the same; the following works for me with rails 3.1.0, rspec 2.6.0, capybara 1.1.1:
page = Capybara::Node::Simple.new( rendered )
page.should have_content( "blah" )
You can't call capybara's methods on rendered, that's just a string. You can use Capybara's string method though to wrap rendered in a Capybara node. Then, you can call Capybara's methods on that node:
describe "some page" do
it "should render with lots of stuff" do
assign ..
render
Capybara.string(rendered).should have_button('Any button')
end
end
For more information, check out this post:
http://www.tamingthemindmonkey.com/2011/11/07/capybara-matchers-and-scoping-in-view-specs
At the bottom of this page, in the "Webrat and Capybara" section, it looks like Capybara is unsupported for rspec view specs
http://relishapp.com/rspec/rspec-rails
Updating this old question as things have changed since most of the other answers were added:
Capybara now does support view specs (out of the box) and this is documented on Capybara's master branch.
To quote the docs:
Finally, Capybara matchers are supported in view specs:
RSpec.describe "todos/show.html.erb", type: :view do
it "displays the todo title" do
assign :todo, Todo.new(title: "Buy milk")
render
expect(rendered).to have_css("header h1", text: "Buy milk")
end
end
Support for these without additional let(:page) style code appears to have been added in an earlier version. (It's working for me in capybara 2.4.4 and 2.2).
Note also that only a limited subset of matchers are supported; however you can gain more functionality by using Capybara.string; ex:
expect(Capybara.string(rendered).first('td')).to have_no_content 'Tom Riddle'
You can also use capybara syntax
describe "some page" do
it 'should render hello with name' do
assign(:user, double("User", first_name: "John"))
render
expect(rendered).to have_content("Hello John")
end
end