Rspec testing specific nodes in response - ruby-on-rails

I'd like to isolate specific nodes to test on.
e.g. instead of
get :show
response.should have_content(#user.name)
it would be more descriptive/correct to be able to write something like
get :show
profile = response.find_selector("div.user-profile")
profile.should have_content(#user.name)
is it possible?
UPDATE
Got a bit further with this after reading Peter's answer but still not finding elements.
in app\views\users\index.html.erb
<h1>Users</h1>
<div id="test"></div>
in spec\controllers\users_controller_spec.rb
require 'spec_helper'
describe UsersController do
render_views
it "should should have header" do
get :index
response.should have_selector("h1", content: "Users")
end
it "should show user profile" do
get :index
node = page.find_by_id("test")
p node
end
end
The first test passes, the second test gives ElementNotFound error. I'm possibly just doing something stupid as this is my first go at Rails.

Yes, it is possible. Capybara doesn't have find_selector, but it does have find and derivatives which take a locator and behave as you imply above. See http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Finders
For example, instead of:
page.should have_selector('foo', text: 'bar')
you can say:
node = page.find('foo')
node.should have_content('bar')

Related

'No route matches' error while using Factory Girl on Rails

I've been trying to use FactoryGirl for tests on my Rails application, but I'm running into difficulty with it.
I feel as if there must be something fairly obvious I'm doing wrong, but after much searching I haven't been able to figure out the cause.
I'm trying to run a test to confirm the 'show' action is successful on one of my controllers.
Here's the error message I'm getting:
Failure/Error: get 'show'
ActionController::UrlGenerationError:
No route matches {:action=>"show", :controller=>"simple_requests"}
Below are the relevant code snippets leading to this outcome.
/spec/controllers/simple_requests_controller_spec.rb
require 'spec_helper'
describe SimpleRequestsController do
describe "GET 'show'" do
before do
#simple_request = build(:simple_request)
end
it "should be successful" do
get 'show'
expect(response).to be_success
end
end
end
/factories/simple_requests_controller_spec.rb
FactoryGirl.define do
factory :simple_request do
id 123
full_name "Testie McTesterson"
company "Test Company"
role "Analyst"
email "foobar#foobs.com"
phone "000888"
message "Test question?"
end
end
/controllers/simple_requests_controller.rb
def show
authorize SimpleRequest #For pundit
#simple_request = SimpleRequest.find(params[:id])
end
I have two hypotheses as to why this may be happening:
1) Rspec is looking for an id for the 'show' action, but somehow can't find it. (Although there is one in the Factory, and I've yet to figure out how it wouldn't be flowing through.)
2) Pundit is causing issues, since the show action may require authorization (although commenting out the 'authorize' line makes no difference at present)
Any and all thoughts welcome :)
EDIT
Pasting below the output of rake routes | grep simple_requests
simple_requests GET /simple_requests(.:format) simple_requests#index
POST /simple_requests(.:format) simple_requests#create
new_simple_request GET /simple_requests/new(.:format) simple_requests#new
edit_simple_request GET /simple_requests/:id/edit(.:format) simple_requests#edit
simple_request GET /simple_requests/:id(.:format) simple_requests#show
PATCH /simple_requests/:id(.:format) simple_requests#update
PUT /simple_requests/:id(.:format) simple_requests#update
DELETE /simple_requests/:id(.:format) simple_requests#destroy
Edit 2 - Adding ID parameter
I have now also attempted to add an id as follows:
it "should be successful" do
get 'show', id: #simple_request.id
expect(response).to be_success
end
This time I received the following error message
ActiveRecord::RecordNotFound: Couldn't find SimpleRequest with 'id'=123
'123' is the ID in my /factories - I think I must be missing something to get this working, but can't figure out what yet.
Your SimpleRequest does not have an Id. You need to use create instead of build
before do
#simple_request = create(:simple_request)
end
it "should be successful" do
get 'show', id: #simple_request.id
expect(response).to be_success
end
Try this:
before do
#simple_request = create :simple_request
end
it "should be successful" do
get 'show', id: #simple_request.id
expect(response).to be_success
end
It's a show view, so you'll have to supply your request with an ID and you'll have to actually create a record with create instead of build in your before block.

Testing page redirect in rails

How can I test this in rails:
A student goes to a list of their groups, and when they click the link, it brings them to that group page.
I have the gist of it down, but how can I test that it went to a page? I was thinking something along the lines of:
expect(page).to eq()
but then I am not sure what to do from there. I don't want to just expect the page to have the name of the group, because that that would be on the overall list and the page the link would bring them to. Any advice?
You're looking for the redirect_to() method. So an example would be:
require 'spec_helper'
describe FoobarController do
describe "GET foo" do
it "redirects to bar" do
get :foo
expect(response).to redirect_to(bar_path)
end
end
end

Rspec matching ActiveRecord result sets

UPDATED: I realise now that I've been misreading the diff, and I have a string or symbol on one side of the comparison. Still unsure how I should be putting the expectation in this test however..
I'm new to Rspec and TDD in general, and I've run into this problem. I have a controller that does this:
def index
#users = User.page(params[:page])
end
(I'm using Kaminara to paginate)
And a spec:
describe "when the user DOES have admin status" do
login_admin_user
it "should allow the user access to the complete user list page" do
get :index
response.response_code.should == 200
end
describe "and views the /users page" do
before(:each) do
User.stub(:page) {[ mock_model(User), mock_model(User), mock_model(User) ]}
end
it "should show all users" do
get :index
assigns (:users).should =~ User.page
end
end
end
The spec fails with the following:
Failure/Error: assigns (:users).should =~ User.page
expected: [#<User:0x5da86a8 #name="User_1004">, #<User:0x5d9c90c #name="User_1005">, #<User:0x5d93ef6 #name="User_1006">]
got: :users (using =~)
Diff:
## -1,4 +1,2 ##
-[#<User:0x5da86a8 #name="User_1004">,
- #<User:0x5d9c90c #name="User_1005">,
- #<User:0x5d93ef6 #name="User_1006">]
+:users
Those result sets look identical. Why does this spec fail? Thanks in advance!
I think the problem is the space after assigns. It's comparing the symbol :users to your list. Change it to:
assigns(:users).should =~ User.page
And just a note on how to read Rspec failures. The part after expected, is what you gave to should, whereas the part after got is the value your code actually produced. So it's clear from the report that the result sets were not identical.

DRY controller specs with RSpec

I'm currently struggling a bit trying to keep my controller specs DRY and succinct and down to one assertion per example. I'm running into some difficulties particularly with where to place the actual controller request call within a structure nested to match the various edge cases.
Here's an example, simplified to demonstrate the problem:
describe MyController do
let(:item) { Factory(:item) }
subject { response }
describe "GET #show" do
before(:each) do
get :show
end
context "published item" do
it { should redirect_to(success_url) }
end
context "unpublished item" do
before(:each) do
item.update_attribute(published: false)
end
it { should redirect_to(error_url) }
end
end
end
Clearly this is a contrived example, but it illustrates what I'd like to do and what's not working. Mainly, the before block under the "unpublished" context is the problem. What happens is the change I made to the setup data actually happens after the get call due to the way the contexts are nested, so the example in that context is actually working with the initial scenario rather than the one I intend.
I understand why this happens and how contexts nest. I guess what I'd like to have is some way to tell RSpec what I'd like it to run right after any before hooks yet right before any assertions within a given context. This would be perfect for controller specs. I'd like to take advantage of nesting in my controller specs to gradually build up variations of edge cases without having to scatter the get call or even a call to a do_get helper into each of my it assertions. This would especially get annoying to keep in sync with any custom it_should macros I'm using.
Is there anything in RSpec currently to accomplish this? Are there any tricks I can use to get close? It would seem perfectly suited to the way I've seen a lot of people writing their controller specs; from what I've found, people have basically settled for having do_get helpers called before every assertion. Is there a better way?
The DRY principle states that "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system." What you're doing is much more about saving a few characters here and there than keeping things DRY, and the result is a tangled web of dependencies up and down a hierarchy that, as you can see, is a bitch to get to do what you want it to and consequently fragile and brittle.
Let's start with what you've got written out in a way that's verbose and works:
describe MyController do
describe "GET #show" do
context "published item" do
it "redirects to the success url" do
item = Factory(:item, published: true)
get :show, :id => item.id
response.should redirect_to success_url
end
end
context "unpublished item" do
it "redirects to the error url" do
item = Factory(:item, published: false)
get :show, :id => item.id
response.should redirect_to error_url
end
end
end
end
Now the only "pieces of knowledge" that are being duplicated are the names of the examples, which could be generated by the matchers at the end of each example. This can be resolved in a readable way by using the example method, which is an alias of it:
describe MyController do
describe "GET #show" do
context "published item" do
example do
item = Factory(:item, published: true)
get :show, :id => item.id
response.should redirect_to success_url
end
end
context "unpublished item" do
example do
item = Factory(:item, published: false)
get :show, :id => item.id
response.should redirect_to error_url
end
end
end
end
There. DRY. And quite readable and easy to change. Now, when you happen to add more examples for either of the contexts, you can add a let:
describe MyController do
describe "GET #show" do
context "published item" do
let(:item) { Factory(:item, published: true) }
example do
get :show, :id => item.id
response.should redirect_to success_url
end
example do
# other example
end
end
# ...
end
end
Now the only duplicated code (not the same as the DRY principle) is the get. If you really feel strongly about it, you can delegate those calls out to a method like get_show(id) or some such, but it's not really buying much at that point. It's not like the API for get is going to change from under you, and the only argument to get is the item's id, which you actually care about in the example (so there's no unnecessary information).
As for using subject to capture the response and get one-liners out of the deal, that just makes things really difficult to read and doesn't save you much. In fact, I've come to consider using subject in this way to be a smell.
Hope this all helps.
Cheers,
David
Will
context "unpublished item" do
let(:item) do
Factory(:item, published: false)
end
it { should redirect_to(error_url) }
end
work for you? BTW, before by default is before(:each) so you can DRY you specs a little more.
UPDATE:
you can also isolate examples with anonymous contexts, like:
describe "GET #show" do
let(:show!) do
get :show
end
context do
before { show! }
context "published item" do
it { should redirect_to(success_url) }
end
# another examples with show-before-each
end
context "unpublished item" do
before do
item.update_attribute(published: false)
show!
end
it { should redirect_to(error_url) }
end
end

Rspec conditional assert: have_content A OR have_content B

I know this would be a really newbie question, but I had to ask it ...
How do I chain different conditions using logic ORs and ANDs and Rspec?
In my example, the method should return true if my page has any of those messages.
def should_see_warning
page.should have_content(_("You are not authorized to access this page."))
OR
page.should have_content(_("Only administrators or employees can do that"))
end
Thanks for help!
You wouldn't normally write a test that given the same inputs/setup produces different or implicit outputs/expectations.
It can be a bit tedious but it's best to separate your expected responses based on the state at the time of the request. Reading into your example; you seem to be testing if the user is logged in or authorized then showing a message. It would be better if you broke the different states into contexts and tested for each message type like:
# logged out (assuming this is the default state)
it "displays unauthorized message" do
get :your_page
response.should have_content(_("You are not authorized to access this page."))
end
context "Logged in" do
before
#user = users(:your_user) # load from factory or fixture
sign_in(#user) # however you do this in your env
end
it "displays a permissions error to non-employees" do
get :your_page
response.should have_content(_("Only administrators or employees can do that"))
end
context "As an employee" do
before { #user.promote_to_employee! } # or somesuch
it "works" do
get :your_page
response.should_not have_content(_("Only administrators or employees can do that"))
# ... etc
end
end
end

Resources