I'm learning RSpec 2 with Rails 3 and while it's been going along quite nicely so far, I'm having a problem testing the helper link_to_unless_current in a view. What I've been trying to do is use a simple assert_select from a view spec to determine if a link is being generated in the following partial view (HAML):
%article.post{ :id => "post-#{post.id}" }
%header.post-title
%h2= link_to_unless_current post.title, post
.post-content= raw post.body
However, I don't know how to get the view spec to recognize what "current" means because it's a view spec and it only tests the view, not the request. I know this would be a lot simpler in a controller spec, but I think that I should be testing what a view does in its spec and that moving this test out to a controller spec would be confusing things a lot. What I'm asking is: is there any way to tell the view spec, perhaps in a "before" block, what the current page is? Also, am I doing the right thing in respect to organizing my tests? Should this test rightfully reside in a controller spec?
Never mind, I eventually figured it out. You have to stub UrlHelper.current_page? and have it return true if the url options passed match the page you want to act as the current page:
view.stub("current_page?".to_sym) {|options| url_for(options) == post_path(#post) }
I still don't know if this is the way I should be doing RSpec tests, but, whatever, this works. :P
Related
Let's say I'm testing the action index of the controller StaticPages. On the controller tests, I need to count the number of links to about_path on this page.
I've Google it and found nothing that could help me. I know this is quite simple and a newbie question but I just started to study RSpec and I stuck here.
First, you need to keep in mind that controller specs don't render views by default. I recommend that you either use a view or request spec.
Second, if you're using builtin assertions then you can check the number of links with:
assert_select %(a[href="#{about_path}"]), 5 # 5 is the expected number of links
If you use expectations then the following should work:
expect(page).to have_selector(%(a[href="#{about_path}"]), count: 5)
I recommend you take a look at the RSpec docs.
You could parse the response body, then count.
doc = Nokogiri::HTML(response.body)
doc.css('a').length
I want to have a user menu in my layouts/application.html.erb. In future, I plan to move it to a partial layouts/_user_menu.html.erb.
How should I start, write tests for this menu just in application.html.erb_spec.rb, and then as refactoring to move it to partial, leaving all tests in application.html.erb_spec.rb?
Or write its separate test _user_menu.html.erb_spec.rb? In this case, how can I test application.html.erb to render this partial? I don't thinks its good idea to use html selectors here, and think about something like in my application.html.erb_spec.rb:
expect(view).to render_partial 'user_menu'
Please try this
response.should render_template(:partial => 'partial_name')
I've created a few apps using Ruby on Rails now, but there's a few concepts that I haven't quite been able to get my head around.
One of these is how does the 'routing' process work? By that I mean, where a user enters a URL string and Rails serves the relevant assets in response to the URL.
Here's what I think is happening:
A user browses to the server using their browser:
http://0.0.0.0:3000
They then prepend their URL with a string:
http://0.0.0.0:3000/entries/view_all
Rails' 'routes.rb' file specifies what the string should actually relate to via directives:
match "/entries/view_all" => "entries#view_all"
This above directive says that when the string "/entries/view_all" is prepended to a URL, execute the method view_all, found in the file 'entries_controller.rb'.
The view_all method is executed:
def view_all
#entries = Entry.all(:order => 'created_at DESC')
end
It assigns all the entries from the table 'Entry' to the constant #entries, in descending order.
Rails then magically knows to serve the user the 'view_all.html.erb'.
An each loop inside the 'view_all.html.erb' displays relevant information from the 'Entry' table:
<% #entries.each do |entry| %>
<h1><%= entry.title %></h1>
<p><%= entry.content %></p>
<p><em>Posted at <%= entry.created_at %></em></p>
<% end %>
My questions are as follows:
How wrong is my concept of how things work?
In step #3 above, how does Rails actually know the view_all method is found inside 'entries_controller.rb'? The directive was entries#view_all, not entries#view_all. Does Rails automatically match the start of the controller file names inside the 'controllers' directory, and ignore the '_controller.rb'?
In step #6 above, how does Rails 'magically' know to serve the 'view_all.html.erb' view? Is it similar to how I think it works in question #2? Does Rails match the 'view_all' part of the file name with the name of the method found in 'entries_controller.rb'?
How is the object/constant #entries, and all its methods, "passed on" to 'view_all.html.erb' from 'entries_controller.rb'?
In response to (2) and (3) - Rails emphasizes convention over configuration, which results in seemingly magical coupling between the router, controllers, and view templates. For example, the router knows that entries refers to an EntriesController class because there's a line in ActionDispatch::Routing::RouteSet:
def controller_reference(controller_param)
controller_name = "#{controller_param.camelize}Controller"
...
It's not magic - the "Controller" word is hard coded. It's just what Rails was programmed to expect given your inputs. And it's like this all over the place, which can be a bit daunting to comprehend when you're starting out with it (have a look at Ember.js for even more daunting magic like this).
In response to (4): Rails copies your individual instance variables into an ActionView instance. There is fairly significant contention in the community over whether it should really be doing this, but for now that's how it works, and you should keep that in mind when you are writing your controller actions. You don't want a lot of overhead in copying numerous or bloated objects that you don't need in the view.
You got it, actually. It's all name matched. The Entry model matches up with the entries_controller which matches up with the entries view. The name of the specific view correlates with the controllers action.
#entries is an instance variable (called such as it's an instance of the whole model), as is any variable that starts with an #. Those variables in the controller are the ones that are available to the corresponding view.
A neat trick to use is the _enrty partial.
_entry.html.erb
<h1><%= entry.title %></h1>
<p><%= entry.content %></p>
<p><em>Posted at <%= entry.created_at %></em></p>
Then in places that you want to call the index (your view_all is typically labeled as index), or parts there of, you can <%= render #entries %>
But yeah, a lot of the magic of rails is in the matchy matchy naming conventions. There are ways around that, as everything is customizable, but that sums it up. Cheers!
1) How wrong is my concept of how things work?
Thats a pretty good way of thinking about how the process works
2) In step (3), how does rails actually know the 'view_all' method is found inside 'entries_controller.rb'? The directive was 'entries#view_all', not 'entries#view_all'. Does rails automatically match the start of the controller file names inside the 'controllers' directory, and ignore the '_controller.rb'?
Rails abides by a directive called "Convention over configuration", this means Rails will behave a certain way, as long as you give it instructions in the way it expects. So in your query above, because you specify the "entries" part of your controller it knows to go look in "entries_controller" for the "view_all" action
3) In step (6) how does rails 'magically' know to serve the 'view_all.html.erb' view? Is it similar to how I think it works in question 2? Does rails match the 'view_all' part of the file name with the name of the method found in 'entries_controller.rb'?
Convention over configuration aka "magic". Once Rails executes an action, it will look for the matching template to execute based on your request. if you made a request for an json page (by altering your request headers for example), it would look for view.json.erb. If you left out that template it would throw an error unless at the end of the action you used a render call to tell it to do something else
4) How is the object/constant '#entries', and all its methods, 'passed on' to 'view_all.html.erb' from 'entries_controller.rb'?
It just is :D, or are you asking to see the rails source code that is responsible for that?
ActionView (Views) and ActionController(Controllers) both inherit from Actionpack, so I imagine its not that hard to share variables between the two.
I am testing a view in my rails 3.2 application with rspec.
I have wrote tests for my view to include some additional input fields, and they correctly failed. However, after adding the desired input fields, the tests still fail the same way. They output the form in the terminal, and it is as if I hadn't changed anything in the views.
When inspecting the view in the browser, the fields are in fact there, so the tests should pass.
Has rspec not loaded the latest views?
Here is some code (I have reduced it to two fields):
it "renders the form to sign up" do
rendered.should have_selector("form", action: "/users", method: "post") do |form|
form.should have_selector("input#user_email", name: "user[email]", type: "email")
form.should have_selector("input#user_city", name: "user[city]", type: "text")
end
end
The email input is an old input that I had before, and it does recognize it. The city input is new, and it doesn't even appear in the terminal's view output.
What am I doing wrong?
Wow, this was a tricky one.
I am using devise and wanted to test its views. In my view tests, I said:
describe "devise/registrations/new" do
# code here
end
However, the other day I switched to scoped views, and changed the view folder name from devise to users accordingly. Scoped views are good if you have different user models such as user and admin and need different views for them. You can turn on scoped views in config/initializers/devise.rb by replacing the existing scope line with:
config.scoped_views = true
Hence, I also had to change my spec description to
describe "users/registrations/new" do # change to "users" instead of "devise"
# code here
end
Since I didn't do this, it still rendered devise's standard views, which it renders when it doesn't find any views in my views folder - hence the source code output in my terminal, that suggested it could render the right views, but lacked the updated code.
I'm working on a functional test that needs to assert that a certain XHTML tag is present in a certain set of controllers. I'm basically doing this:
class ApplicationControllerTest < ActionController::TestCase
def setup
#controller = ApplicationController.new
end
# [...]
def test_foo_bar_and_baz_include_stylesheet
[FooController, BarController, BazController].each do |controller|
#controller = controller.new
get :show
assert_select 'head > link[rel="stylesheet"]'
end
end
end
The problem is that not all controllers have a :show action. What I need to do is ask either the controller or the routing configuration for the controller's default action and call get with that.
Update: Joseph Silvashy was right: I ended up separating my controller gets out into separate test cases. Solving the "default" route problem was bad enough, until I discovered that some of my routes had conditions attached, and I would have to parse them to craft the correct get call. And finally, Rails functional tests don't deal very well with calling get multiple times in the same test case, especially when that those calls are hitting multiple controllers. :(
I think the lesson here is one that we all know by heart but sometimes is hard to accept: if the code looks hairy, you're probably doing it wrong. ;)
Controllers don't have a "default" view or action, additionally actions can be named anything you want, they don't have to be the standard index, show, new, etc...
I'll probably have to :get the appropriate action for each controller you want to test. It's likely each test will be different down the road anyhow, even though right now they all have the same requirement, I think it makes sense to write one for each action regardless.
try to use the respond_to function: http://www.ruby-doc.org/core/classes/Object.html#M001005
to check if a method exitst.