How to correctly test one object on the page - ruby-on-rails

I'm creating rails application with posts and likes.
feature "Create like" do
include_context "user signed in"
def have_updated_likes_count
new_likes_count = bonus.likes_count
have_content("Likes #{new_likes_count}")
end
let!(:bonus) { create(:bonus) }
let(:decorated_bonus) { bonus.decorate }
before { visit root_path }
scenario "User likes bonus", js: true do
find(".like").click
expect(bonus).to have_updated_likes_count
expect(page).to have_content(current_user.full_name)
end
end
Bonus = post. As u see here I check if User likes bonus. But in this root_path (main page) I have a lot of posts (20), and each of post has likes. My test trying to check expect(bonuses).to have_updated_likes_count and expect(page).to have_content(current_user.full_name) and it always true both, because it check full page, but I need to check only one post (where I did find(".like").click). How can I solve my problem?

It depends on what your page structure is but generally you want to scope your actions to a specific element using within.
page.within('selector that finds the post element') do
find(".like").click
expect(bonus).to have_updated_likes_content # issues
expect(page).to have_content(current_user.full_name) # issues
end
That answers your question, however the rest of the test has a few issues you'll need to work out too.
Once you click the .like element the bonus object gets updated asynchronously, so trying to access it immediately is going to be flaky and you should really be checking for some other visual change on the page before checking it.
You're not reloading the bonus object from the database so it'll probably still have the same value

Related

rspec: undefined method `testimonials' for nil:NilClass

I'm new to Rspec and Capybara. The error I'm getting is Navigation from homepage Visit Gallery
Failure/Error: visit root_path
NoMethodError:undefined method 'testimonials' for nil:NilClass
I tried let two different ways in order to define a variable in the spec. I've added both of them so I can get feedback on what I'm doing wrong.
class WelcomeController < ApplicationController
def index
#event = Event.last
#event.testimonials.first ? #latest_testimonial = #event.testimonials.first.id : #latest_testimonial = nil
end
end
feature 'Navigation from homepage' do
before :each do
visit root_path
find('#nav-menu').find('h1').click #opens up navigation bar
end
scenario 'Visit Gallery' do
find('#nav-gallery').find('.no_bar').click
let(:event) {Event.last} #1st attempt at solving Rspec error.
let(:event) {mock_model(Event, id: 3, name: "Jack & Jill", date: "2004-06-10", created_at: "2014-03-10 02:57:45", updated_at: "2014-03-10 02:57:45")} #2nd attempt at solving Rspec error.
controller.stub(:event)
expect(page).to have_css 'img.photos'
end
end
The other answer is correct - here is essentially the same content in simpler (or maybe just more drawn out by explaining things you already know) terms.
The first thing your test does is execute the before block, which visits root_path, presumably calling the index action on WelcomeController. The first thing that method does does is call Event.last, which returns nil, because your test database is empty so there is no last record to return. Then when you call testimonials on #event, you get the error you see because #event is nil.
To remedy this, you need to create an Event record in the database before you navigate to the root_path and call the index action. One way to do that would be to add this line before visit root_path:
Event.create(name: "Jack & Jill" [...])
That will create a record in the database, so Event.last will return something.
You would also get rid of both let statements and the controller.stub thing. Those are unnecessary now (and had other problems anyway). That should be enough to get that code to at least run.
In practice, you won't find just creating records like I've shown here to be a sustainable approach - that's where factories (with a tool like FactoryGirl) or mocks/stubs come in. Then you use let to define those items just once in your before block, and still limit the overhead consumed by creating them to those subsequent tests where they are actually used.
Regardless, the main point is that the setup of objects (and records if needed) needs to be done before you start triggering controller actions that assume those objects exist.
Event.last is returning nil.
The typical (and easiest) way to do this would be to just create an event in your test, before trying to visit the site, and then your controller will use it. Feature specs should really contain the minimal amount of mocking and stubbing - they represent a real user interacting with the system.
Your stubs are not getting used (and in fact are errors in themselves). This is verified by the fact that your error is coming before they even get set.

How to understand which user cucumber tests are referring to when writing in the first person?

I'm trying to write my current applications cucumber features in the first person, for example:
Given I am logged in as a user
And Mark is a user
When I send him a friend request
Then he should have a friend request from me
And he should receive an email
I'm struggling to properly write the steps to make sure I'm referencing the right user with 'him', 'he' and 'me'.
I could handle:
When I send Mark a friend request
By using:
When /^I send (.+) a friend request$/ do |user|
#user = User.find_by_email(user.downcase + "#test.org")
visit '/users/' + #user.id.to_s
click_button "Add Friend"
end
But would much rather be able to use 'him' or 'he'.
I'm thinking to achieve that I should really be assigning #second_user or some other variable in the step '(.+) is a user'.
Is that correct?
Any advice on this greatly appreciated!
Honestly I think this will end up being more trouble than it's worth. Sitting here trying to come up with a solution, I can't think of anything that wouldn't end up becoming a burden eventually, in the case of re-using those step definitions in future scenarios, or even the mental tax for future readers trying to figure out the flow of execution.
I actually prefer the explicitness of using the name at every step rather than the pronoun. In my experience, especially when interactions between entities become more complex, it's actually really helpful to refer to 'Alice' and 'Bob' instead of 'him' and 'me' or 'User1' and 'User2', in order to follow what's happening.
You need to catch Mark at the second step:
And Mark is a user
This step must be verified, i.e. Mark must exist. Then you can refer him in third step.
How about in Features:
Given I am logged in as a user
And A user exists with name "Mark"
When I send him a friend request
Then in user_steps.rb
Given /^I am logged in as a user$/ do
pending #blah blah
end
Given /^A user exists with name "([^"]+)"$/ |username| do
#user = User.find_by_email(username.downcase + "#test.org")
#user.should be_true
end
When /^I send (\w+) a friend request$/ do |user|
user = #user
# visit blah blah
end

RSpec and Capybara setup, have_no_content doesn't seem to be working

I'm creating an app where I have a simple delete record on the index of the records once a user logs in. When I don't have the user logging in under my rspec specs, the delete works. Meaning that the has_no_content test returns true. But once I put in place the authentication required to access the index of records, the test fails. Any thoughts?
before do
#records = Factory(:record)
login_user
end
it "should delete a record" do
visit records_path
find("#record_#{#records.id}").click_link 'Delete'
page.should have_content "Record has been deleted"
page.should have_no_content "Record 1"
end
Nevermind - I figured it out with the help of using launchy.
My initial thought was that I was some how losing the "session" and that once the delete link was clicked, then the login page was being displayed again. That wasn't the case at all. I did notice though that the header of my app also contained "Record 1" in it. Therefore the spec would fail for obvious reasons. Head to desk - sorry about that, but I figured I'd post my own stupidity and looking way deeper than I needed to.

how to test a stale link click (or link I can't get to) using capybara/rspec

Artists cannot rate their own artworks
it "should display error if voting on own artwork", :js => true do
sign_in
visit "/upcoming"
click_link "like_post_1"
page.should have_content("Can't vote on your own artwork")
end
This was passing just fine.
However, I can't click on like_post_1 anymore because I added a feature to prevent voting links from appearing next to your own artworks.
Does this mean I no longer need test coverage for this scenario because it's extremely rare that someone can click on a voting link for their own artwork? Or should still have coverage to test the ajax response, because it's not tested anywhere else and it's possible for some stale page of links to somehow exist in a tabbed browser window. If so... how do I test it if I cannot call click_link?
I could try to create a POST request to create the vote, but capybara doesn't support posts, and I can't test the ajax response that way...
Or is there a way to simulate tabbed browsing in capybara?
Suggestions?
You can use CSS display:none or visibility:hidden for the own artwork instead of eliminating the link from the DOM. You might have to set
Capybara.ignore_hidden_elements = false
Another way is giving up Capybara and putting them into the controller/model spec. Controller/model spec might be a better place for the extremely rare case or safeguarding your app case.
Please try this:
Use sleep function.
sleep 10 use to wait process upto 10 seconds..
it "should display error if voting on own artwork", :js => true do
sign_in
visit "/upcoming"
click_link "like_post_1"
sleep 10
page.should have_content("Can't vote on your own artwork")
end

Ruby on Rails functional testing with the RESTful Authentication plugin

I started writing functional tests for my rails app today. I use the RESTful authentication plugin. I ran into a couple confusing things I hope someone can clarify for me.
1) I wrote a quick login function because most of the functions in my rails app require authentication.
def login_as(user)
#request.session[:user_id] = user ? user.id : nil
end
The issue I see with this function, is it basically fakes authentication. Should I be worried about this? Maybe it is okay to go this route as long as I test the true authentication method somewhere. Or maybe this is terrible practice.
2) The second confusing thing is that in some places in my functional tests, I need the full authentication process to happen. When a user is activated, I have the do_activate method create some initial objects for the user. It is analogous to the creation of a blank notebook object and pen object for a student application, if that makes sense.
So in order to properly test my application, I need the user to hit that activation state so those objects are created. I am currently using Factory Girl to create the user, and then calling the login_as function above to fake authentication.
I guess another option would be to skip the full authentication sequence and just create the blank objects with Factory Girl. I could test the proper authentication somewhere else.
What do you think? If I should go through the proper sequence, why isn't the code below invoking the do_activate function?
user = Factory.create(:user)
user.active = 1
user.save
Thank you!
Faking it is perfectly acceptable.
However, write other tests that ensure that the things you want protected are protected. So
test "it should show the profile page" do
user = Factory(:user)
login_as(user)
get :show, :id => user
assert_response :success
end
test "it should not show the profile page cos I'm not logged in" do
user = Factory(:user)
get :show, :id => user
assert_response :redirect
end
Feel free to hit me up for followups!

Resources