I have a long form, and for 3 of the fields, I need to test whether those were filled out right or wrong individually, because errors lead to very different actions. But it's very tedious and not DRY to write integration tests like this:
# Test 1
describe "test where field3 fails" do
before do
fill_in "field1", with: "passing info"
fill_in "field2", with: "passing info"
fill_in "field3", with: "not passing info"
...
end
it "should lead to an error specific to field3" do
...
end
end
# Test 2
describe "test where field2 fails" do
before do
fill_in "field1", with: "passing info"
fill_in "field2", with: "not passing info"
fill_in "field3", with: "passing info"
...
end
it "should lead to an error specific to field2" do
...
end
end
# Test 3
describe "test where field1 fails" do
before do
fill_in "field1", with: "not passing info"
fill_in "field2", with: "passing info"
fill_in "field3", with: "passing info"
...
end
it "should lead to an error specific to field1" do
...
end
end
In that case ,I would suggest to write data driven tests like below
describe "test all form fields" do |data|
before do
fill_in "field1", with: data[0]
fill_in "field2", with: data[1]
fill_in "field3", with: data[2]
...
end
it "should lead to an error message" do |CorrespondingErrorMsg|
...
end
This way all the combinations will be defined inside test data ,not in the code so in future if no. of validations even increase or decrease you don't have to touch the code.Only the data.
I hope it makes sense to make the solution dry.
Related
render( json: UserSerializer.response_error(current_user.errors.messages).to_json)
user_serializer.rb
class UserSerializer < ActiveModel::Serializer
attributes :id, :name, :email
def self.response_error(error)
error
end
end
It gives the response as below:
[
"Password can't be blank"
]
But I need to display
"Password can't be blank"
I tried to use "full_messages" but the result was same
def self.response_error(error)
error.join(', ')
end
You could do it like this. The issue is it needs to be an array. Since there could be multiple errors like.
[
"Email can't be blank",
"Password can't be blank"
]
This will be result with the above code in:
"Email can't be blank, Password can't be blank"
In general an array is easier to work with in the frontend regardless of one or many errors.
When you are absolutely sure there will only this one error you could also do
error.join('') to just get the string.
i want to do something like this:
feature "sign-up" do
before {visit signup_path}
let(:submit) {"Create my account"}
feature "with invalid information" do
scenario "should not create a user" do
expect {click_button submit}.not_to change(User, :count)
end
end
feature "with valid information" do
scenario "should create a user" do
fill_in "Name", with: "test name"
fill_in "Email", with: "test#test.com"
fill_in "Password", with: "password"
fill_in "Confirmation", with: "password"
expect {click_button submit}.to change(User, :count).by(1)
end
end
end
but when i run rspec i get
in `block in <top (required)>': undefined method `feature' for #<Class:0x000000039e0018> (NoMethodError)
if i change it a bit to look like this it works:
feature "with invalid information" do
before {visit signup_path}
let(:submit) {"Create my account"}
scenario "should not create a user" do
expect {click_button submit}.not_to change(User, :count)
end
end
feature "with valid information" do
before {visit signup_path}
let(:submit) {"Create my account"}
scenario "should create a user" do
fill_in "Name", with: "test name"
fill_in "Email", with: "test#test.com"
fill_in "Password", with: "nirnir"
fill_in "Confirmation", with: "nirnir"
expect {click_button submit}.to change(User, :count).by(1)
end
end
EDIT:
plus, the following code works(describe nested inside feature) - but is it wrong in any way?
feature "sign-up" do
background {visit signup_path}
given(:submit) {"Create my account"}
scenario "with invalid information" do
expect {click_button submit}.not_to change(User, :count)
end
describe "with valid information" do
background do
fill_in "Name", with: "test name"
fill_in "Email", with: "test#test.com"
fill_in "Password", with: "password"
fill_in "Confirmation", with: "password"
end
scenario { expect {click_button submit}.to change(User, :count).by(1) }
scenario "after submission" do
click_button submit
page.html.should have_content("Registration successful")
end
end
end
EDIT (23/01/2014): Nested features are available since version 2.2.1. See here
EDIT (24/07/2013): Nested features will be allowed in Capybara > 2.1.0. See here
You can't. This is what the mantainer of the gem says about it
I guess you could call this a limitation. feature can not be nested.
You can use either context or describe instead, but I would
suggest not going wild with these, it tends to make tests pretty
unreadable.
In some other cases the convenience of this might be argued but in this specific one you should use scenario instead of the nested feature.
Also if you want to consistent and use the new DSL everywhere, use background instead of before and given instead of let. Like this:
feature "sign-up" do
background {visit signup_path}
given(:submit) {"Create my account"}
scenario "with invalid information" do
expect {click_button submit}.not_to change(User, :count)
end
scenario "with valid information" do
fill_in "Name", with: "test name"
fill_in "Email", with: "test#test.com"
fill_in "Password", with: "password"
fill_in "Confirmation", with: "password"
expect {click_button submit}.to change(User, :count).by(1)
end
end
You have to remove the it because scenario is just an alias for it and you cannot nest it either.
Or you can always switch back to the old DSL if you find it more readable. In that case I would do something like this:
describe "sign-up" do
before {visit signup_path}
let(:submit) {"Create my account"}
context "with invalid information" do
it "does not create a user" do
expect {click_button submit}.not_to change(User, :count)
end
end
context "with valid information" do
before
fill_in "Name", with: "test name"
fill_in "Email", with: "test#test.com"
fill_in "Password", with: "password"
fill_in "Confirmation", with: "password"
end
it "creates a user" do
expect {click_button submit}.to change(User, :count).by(1)
end
end
end
But as long as the spec checks what it has to check, you should be fine anyways. The rest is all a matter of style, readability and good practices, which are important but more open to discussion and disagrement. In this case the author of the gem didn't allow nested feature's. Maybe for readability or maybe didn't feel it was needed or maybe didn't think about it... If you really want to nest features you can always try to implement it and pull request it.
I'm trying to use capybara+rspec and get this error: Unable to find field "Name" (Capybara::ElementNotFound)
Here is my form:
%h2 Sign up
= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => {:class => 'form-vertical' }) do |f|
= f.error_notification
= display_base_errors resource
= f.input :name, :autofocus => true
= f.button :submit, 'Sign up', :class => 'btn-primary'
= render "devise/shared/links"
Here is my user_steps.rb
When /^I sign up with valid user data$/ do
create_visitor
sign_up
end
def create_visitor
#visitor ||= { :name => "Test visitor"}
end
def sign_up
visit '/users/sign_up'
fill_in "Name", :with => #visitor[:name]
click_button "Sign up"
end
What's wrong????
I encountered this issue and realized that Capybara filtered out hidden fields--my element belonged to a non-active tab (hidden) in a multi-tab page. I just passed the :visible arg and set it to false, and voila! the element was found.
fill_in "content", with: 'foo bar', visible: false
or
find('#item_content', visible: false).set 'foo bar'
It looks like to me that you are looking for a field label Name, but your name field does not have a label, so you probably need to use the ID of the field or the name which is probably:
"#{resource_name}[name]"
Also, as #nmott said in his comment, you should try using save_and_open_page so that you can actually look at the page, However, be aware you need the launchy gem to use this method.
Furthermore, what you might discover is you're not even on the right page. My usual problem when testing pages OTHER than the sign up page is that I've been redirected and didn't know it. So after using visit, you should also use assert_template to make sure you're on the right page.
I have the same problem myself and I had to stop using fill_in all together.
What I did was replacing all occurrences of fill_in with the following code :
element = page.find("Name")
element.set(#visitor[:name])
I guess you can encapsulate this into a method in order to make your tests more smooth
Try to narrow down your scope from within the form as such
within("form#whatever_form_id") do
fill_in "Name", :with => #visitor[:name]
click_button "Sign up"
end
Ok guys I found it out having the same issue , its very simple :
In capybara or rspec they need you to but "Name" and in your form or label field you need to write "name" in small....there you go works for me.
For me it was the next line of the spec that was causing the problem not the fill_in "name" line. It didn't matter whether or not name was "Name" or "name".
The next click_button line for me had click_button "Wrong Name" which was the wrong name for the button, and this did not give the expected error of "can't click on button "Wrong Name" but instead gave can't find field "name".
A bit verbose for my first even post on stack overflow. Bottom line. Consider the line below the line given in the capybara error message.
I'm new in cucumber and rspec, and I'm trying to test a button whose text is "Regístrate" (look at the accent "í").
In my app, I have a lot of controls written in spanish, with accents á é í ó ú. But I get an error because of these vowels with accents. I'm sure there is an easy solution but I don't achieve to find a solution.
I could change the tests descriptions, but not the words in the user application... any suggestion?
describe "signup" do
before { visit signup_path }
let(:submit) { "Regístrate" }
describe "con información válida" do
it "no debería crear el usuario" do
expect { click_button submit }.not_to change(User, :count)
end
end
describe "con información válida" do
before do
fill_in "Nombre", with: "Example User"
fill_in "Email", with: "user#example.com"
fill_in "Password", with: "foobar"
fill_in "Confirmación", with: "foobar"
end
it "debería crear el usuario" do
expect { click_button submit }.to change(User, :count).by(1)
end
end
end
error:
user_signup_steps.rb:3: syntax error, unexpected $end, expecting keyword_end
"Regístrate"
^ (SyntaxError)
Looks like an encoding error. Try adding this line to the top of the spec file:
# encoding: utf-8
I'm doing the ruby on rails tutorial, I specifically adding listing 8.21 to spec/requests/users_spec.rb
require 'spec_helper'
describe "Users" do
describe "signup" do
describe "signup failure" do
lambda do
it "should not make a new user" do
visit signup_path
fill_in "Name", :with => ""
fill_in "Email", :with => ""
fill_in "Password", :with => ""
fill_in "Password confirmation", :with => ""
click_button
response.should render_template('users/new')
response.should have_selector("div#error_explanation")
end
end.should_not change(User, :count)
end
end
end
as far as I can tell, this is exactly like the listing in 8.21; however, when I run
rspec spec/requests/users_spec.rb -e "Users"
I got the following error ...
#> rspec spec/requests/users_spec.rb -e "Users" No DRb server is running. Running in local process instead ... /Users/bryanjamieson/rails_projects/sample_app/spec/requests/users_spec.rb:17:in `block (3 levels) in ': undefined method `change' for
# (NoMethodError)
any help would be appreciated.
I'd use User.count
end.should_not change(User.count)
Depending on which rails version and rspec version you're using (assuming Rails 3.1, Rspec2) the tutorial may be a little of out date