How to test that a form successfully submitted using Rspec? - ruby-on-rails

I'm writing feature specs for my Article form.
In one test, I have RSpec look for certain form inputs using the labels:
expect(find_field("Title")).to_not be_nil
Now I'd like to know if the form was submitted properly. How can I do this using RSpec, if you aren't supposed to check the database from a feature spec?
For example, what if I mistyped the name attribute on the Title input? The label would still be found by my find_field() call, and controller specs would have me specify the title in a hash.

In this case one would assume a Title would be required for an Article so if the name attribute was misspelled the creation of the Article would fail and you'd end up on an error page (Also an unpermitted parameter would raise in the creation by default in test mode with strong parameters). If its a field not required for creation of the Article then the assumption is that the info is displayed in the app somewhere and can be checked for on that page.
As an aside your sample expect would read much clearer if rewritten as
expect(page).to have_field('Title')

There are loads of way to approach this:
expect(page).to have_field('Title')
expect(page).to have_css('#someSelector')
expect(current_path).to eq article_path (for example)
expect(page).to have_content "ABC" (i.e. some content which will be rendered)
The Capybara readme on Github is a really good one full of easy to follow examples - https://github.com/jnicklas/capybara.

Related

Cucumber, Rails: has_content? test passes even though the string isn't there

Scenario is:
Scenario: View welcome page
Given I am on the home page
Then I should see 'Welcome'
And definition of the step is
Then("I should see {string}") do |string|
page.has_content?(string)
end
The test passes whether the word "welcome" is in the home page or not. What am I doing wrong?
A step will only fail if it throws an exception. By its naming convention, the has_content? method returns false if the content is not in the page, and thus does not throw an exception. This will cause your step to "pass" when you intend it to fail.
You need to make an assertion with some sort of unit testing library (my Ruby is a little rusty)
Then("I should see {string}") do |string|
page.has_content?(string).should_be true
end
You'll need something like RSpec to gain access to a library allowing you to make assertions.
Doing this the way shown in the other answer will work but will not give useful error messages. Instead you want
For RSpec
expect(page).to have_content(string)
For minitest
assert_content(string)
For others
page.assert_content(string)
Note that assert_content/assert_text and have_content/have_text are aliases of each other so use whichever reads better.

Is there a better way to test :admin security

I am going through Hartl's Rails Tutorial. I'm up to the first exercise of 9.6, where he asks me to test that the User admin attribute isn't accessible. The justification is earlier in the book:
After Listing 9.42, Hartl's Rails Tutorial says
If we omitted the attr_accessible list in the User model (or foolishly added :admin to the list), a malicious user could send a PUT request as follows:
put /users/17?admin=1
The corresponding exercise (exercise 9.6.1) in the tutorial says
add a test to verify that the User admin attribute isn’t accessible
I have completed that test with this code in user_spec.rb:
expect do
#user.update_attributes(:admin => true)
end.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
But I used stackoverflow to get that test. This was my original idea (in user_pages_spec.rb):
expect do
put user_path(user) + "?admin=1"
end.to raise_error(ActiveModel::MassAssignmentSecurity::Error) # or some other error
But I couldn't get it to work.
So my questions are:
Is my idea possible? Isn't it better to test directly for what a potential hacker might do from the command line? Isn't that the idea of Capybara, testing user actions?
If it is possible, is there a difference between testing mass assignment and testing the PUT action?
If it isn't possible, why? Is it just not necessary or am I missing something here?
I think I would argue with you that your test is actually better. Some would argue that the given answer is testing Rails functionality which really isn't your job. However, I do think it's frequently good to test things in several different directions.
I was under the impression from back in my school days that it was impossible to send data via the URI except when doing a GET. A quick search of stackoverflow didn't result in any confirmation. However, the wikipedia article seems to imply it:
http://en.wikipedia.org/wiki/POST_%28HTTP%2
I think the correct line of code would be
put user_path(user), {user: {admin: 1}, id: user.id}
I hope that helps.

Creating a path with an object id to map with cucumber scenario

I'm trying to create a cucumber scenario that checks to see if elements are loaded for an 'edit posting' page. My trouble, however, is that I don't know how to create a path that will direct it to the page.
The general path is something like: /posting/id/edit
i.e. /posting/11/edit
Here is my posting.feature scenario
# Editing existing post
Scenario: Saving the edits to an existing post
Given I am logged in
Given there is a posting
Given I am on the edit posting page
When I fill in "posting_title" with "blah"
And I fill in "posting_location" with "blegh"
When I press "Update posting"
Then I should see "Posting was successfully updated."
I dabbled around with some Factory Girl stuff, but I don't have the knowledge to use it appropriately (if it offers a solution), and wasn't able to find a relevant example.
I've also seen a lot of suggestions with regards to 'Pickle', but if possible I'd like to avoid that route to keep things simple seeing as I have very limited experience. Thanks!
Is there a link on your website that would take someone to the edit page? Then you could do something like:
Given I am on the homepage
And I follow "Posts"
And I follow "Edit"
This assumes that there is a link on your homepage whose text is Posts, and then another one in the resulting page called Edit. This is the best way to accomplish this, because there should be a direct route to whatever page you are testing. Those steps are also provided in web_steps.rb
You could also make a custom step like you did there with Given I am on the edit posting page and the code would be something like:
Given /^I am on the edit posting page$/ do
visit("/posting/11/edit")
end
Which you of course could also generalize like I am on the edit posting page for posting 11. But in general, cucumber tests are acceptance tests, which means not bypassing things like this. You should have a link to the edit page that can be clicked.
I came up with a solution, but I am not sure of its validity in terms of how clean it is. I ended up using Factory Girl (installed the gem).
I kept my scenario the same.
Under features/step_definitions I created posting_steps.rb
Given /^there is a posting$/ do
Factory(:posting)
end
Under features/support I created a file factories.rb with the following inside:
Factory.define :posting do |f|
f.association :user
f.title 'blah'
f.location 'Some place'
end
In my paths.rb I used
when /the edit posting page/
edit_posting_path(Posting.first)
How it works (or at least how I think it works) is that as
Given there is a posting
is executed, the posting_step.rb is invoked (Factory(:posting) is basically Factory.create(:posting)), which in turn uses the factory definition I created in factories.rb. This leads to an instance of a posting being created.
Then in my paths.rb
when /the edit posting page/
edit_posting_path(Posting.first)
gets passed the id from the instance, to ultimately get a path that could resemble /posting/1/edit , and the test continues on its way!
If there are any corrections to be made, please let me know as I am just learning the ropes.
Hopefully this will help other newbies out there!

Cucumber step ambiguity when using built in 'within' step scoper

I've created a custom cucumber step for checking the destination for a link, and I'm using Cucumber's new built in support in web_steps for scoping these lookups. So I have two cucumber steps involved:
# My step to verify the link
Then /^"([^\"]*)" should link to (.*)$/ do |link_text,page_name|
page.should have_link(link_text, :href => path_to(page_name))
end
# Cucumber's built in step to scope things
# Single-line step scoper
When /^(.*) within ([^:]+)$/ do |step, parent|
with_scope(parent) { When step }
end
I use this by having cucumber scripts that do things like
And "home" should link to the home page within the "Email Signature" section
My problem is that I'm getting ambiguous matches on the above between these two steps, because the 'within' clause can't be told apart from the "the home page", because the latter doesn't have any bounding quotes.
I've tried changing the link step to read like this, thinking it might resolve the ambiguity by not matching the 'within', but I think the 'within' gets swallowed by the preceeding group instead:
Then /^"([^\"]*)" should link to (.*)(?!within)$/ do |link_text,page_name|
page.should have_link(link_text, :href => path_to(page_name))
end
Any thoughts on how to resolve this?
Not directly an answer to the question I posed (for that see Qtax's answer), but here's what I've wound up doing. I think it's a nicer solution anyway, for what it's worth...
I've created a custom version of the scoping helper that looks like this:
Then /^within ([^,]*), (.+)$/ do |parent, step|
with_scope(parent) { When step }
end
This allows me to write steps like this:
And within the "Email Signature" section, "home" should link to the home page
Which I think (a) reads more naturally (it's clearer that we're talking about the link being in the e-mail signature section, not the home page), and (b) works around the problem I was having, because the unquoted 'within' selector is well out of the way of the unquoted page name.
Try something like:
/^"([^"]*)" should link to ((?:(?!within).)+)$/
Don't know anything about Cucumber, I'm just going by what you tried to do.

Ruby on Rails testing: How can I test or at the very least see a form_for's error_messages_for?

I'm working on creating a tests, and I can't figure out why the creation of a model from a form_for is failing in the test but works in real browsers. Is there a straightforward way for me to see what the problems are in the model creation?
Even better would be, is there a straightforward way for me to test the error outputs that I access via error_messages_for? In that case, I'd like to also add in tests that make sure that malformed forms are outputting the correct errors.
You can access the validation errors on an object using #model.errors. Use #model.errors.on(:field_name) to get an array of error messages applied to a particular field. You can also use #model.errors.invalid?(:field_name) in your tests to assert that an error was triggered for a particular field.
For example:
test "should reject invalid post" do
#p = Post.new
assert !#p.valid?
assert_equal #p.errors.on(:title), 'cannot be blank'
end

Resources