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
Related
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
It's a brand new project. Here's the exact commands I've run:
rails new MyProject
bundle install
rails generate controller Image
I've added this one route:
root :to => "image#process"
I've added this function to the ImageController (image_controller.rb)
def process
render :nothing => true
end
And finally I've removed the default index.html. When I run the project, it has an error saying process expects 0 parameters, not 1. So I modify the method to tell me what parameter is trying to be sent to process.
def process(arg)
p arg
render :nothing => true
end
The string "process" is printed to the screen. I've done several Rails projects before and never encountered this. Did I miss a step somewhere? Is this something new in Rails 3.0.10? Or maybe caused by Ruby 1.9.2? I think I usually use 1.8.7.
You can not name an action as process, this is an internal method for rails controllers, name it something else.
There's a bunch of other names you can't use for controller actions like render, params, request. Unfortunately there isn't a list of these things.
For future reference, in case you aren't using it, you can view all internal methods and classes here: ruby doc with nav on top right
Helps me when picking names.
I'm trying to test that "static" pages (they're ERB, but get cached), generated through rails, don't render any stray flash notices left over by the authentication system (Devise) or wherever else.
I've tried writing this controller spec, but it appears that response.body only renders the template, not its layouts?
describe "so that static caching can be used" do
render_views
specify "flash notices are not rendered" do
# edit: the following flash lines don't do anything
# it's not the right flash object, this one is intended for
# inspecting after request not setting before request
flash[:notice] = "flash boo"
flash[:error] = "flash boo"
flash[:alert] = "flash boo"
get :show, :page => 'privacy_policy'
response.body.should have_content('flash boo')
end
end
class StaticPagesController < ApplicationController
layout 'master'
def show
response.headers['Cache-Control'] = "public, max-age=#{6.hours}"
render "static_pages/#{params[:page]}"
end
end
I've tried changing to a layout which does render flash notices, and even inserting the text into the layout template, but can't make the spec fail.
Is there a way to ask rspec to render the template with the appropriate layouts as well?
Is a controller spec the wrong way to try and do this?
It seems out of place, as it's more to do with which layouts are being used, and their contents, but the rendering process starts at the controller, it receives the result, and I can manipulate the flash hash contents before rendering.
Versions:
rails (3.0.10),
rspec-rails (2.6.1),
rspec-core (2.6.4)
Thanks, Nick
Turns out this isn't the right approach. It should be an integration test (cucumber, request spec, etc) as it's testing several layers. That and rails doesn't seem to render the templates inside their layouts at this level.
So in integration test:
Set up a flash message by making a request to a controller which does nothing else (create a dummy controller & routing in your test code), then hit the page of concern and make sure the flash notice isn't rendered.
e.g. https://gist.github.com/1178383
It seems like a long way round but it will cover it.
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
Something like this in application.rb:
# Configure application generators
config.app_generators do |g|
g.form_builder Formtastic::SemanticFormBuilder
end
If I do so I get an error when I try to scaffold a model:
Expected Thor class, got Formtastic::SemanticFormBuilder
Is it possible to set Formtastic as default form builder?
Updated.
I have tried Simple forms and it's really awesome (Thanks to nathanvda). The DSL is almost the same as Formtastic has. The only important difference for me is in customizing button labels. In formtastic it's possible to use resource file (formtastic.yml) to set different labels for the same model and action. Sometimes it's necessary, for example in Devise views. But it costs nothing to switch from formtastic to simple forms even in this case as it's possible to do it in this pretty simple way:
= f.submit t("customized_button_label")
Now about the original question. When I installed simple forms it creates template in lib/templates/haml/scaffold directory which will be used with scaffold. Straightforward.
I am not entirely sure about formtastic, either it does this straight out of the box, so not configuration needed; or not at all.
But what i do know: simple_form does provide scaffolding, even configurable which is totally awesome. The DSL between formtastic and simple_form is close to identical, but with simple_form the level of configuration is much better. You have total control how a form should be scaffolded, you have total control how a single field is turned into html. Pretty awesome.
You can find a quick introduction here.