Where to test routes in ruby on rails - ruby-on-rails

Where to test routes in ruby on rails?
unit tests?
functional tests?
integration tests?
Addition:
To be exact, where to apply assertions described on guides and on api?

Routes should be done as part of integration tests. Integration tests are where you test the important work flows of your application - more specifically whether a URL is defined or not seems to be an important workflow.
Your integration test would look like any normal integration test:
# /tests/integration/routes_test.rb
require 'test_helper'
class RoutesTest < ActionController::IntegrationTest
test "route test" do
assert_generates "/photos/1", { :controller => "photos", :action => "show", :id => "1" }
assert_generates "/about", :controller => "pages", :action => "about"
end
end
As to #jemminger's response of not testing routes - While it is Rail's tests that verify that routes.rb works, it's not Rail's responsibility to test whether http://yoursite.com/users is defined in your routes. The caveat is that most route testing could be done in existing integration tests, so specific tests for routes could be redundant.
The specific use case I can think of are all the people that have already, or are going to upgrade from Rails 2 to Rails 3. The code to define routes has changed significantly, and it's better to find out from tests that the routes were upgraded correctly, than from users when they report 404 errors.

Why do you feel the need to test the routes? Purely to make sure that the routes defined in your routes.rb actually work? If so, then don't. That's not the job of your application's tests to make sure that the framework's internals operate properly - that's the job of the Rails framework's own tests.
If perhaps you have some sort of dynamic/user definable route that you want to test, I'd probably go with integration.

I suggest you create a test file for the routes in your test/controllers folder.
class HomeRoutesTest < ActionController::TestCase
test "must route to home index" do
assert_routing '/', controller: "home", action: "index"
end
end
It was mentioned that they belong to integration test. I disagree. You merely test routes, that's it. So it would rather fall into functional or even unit testing instead of integration testing.
You can find a reference in the Rails RailsGuide Testing, section 9.
Testing Routes
Integration tests are for the flow, where you test the interaction of different controller action, e.g. executing a business process like user logs in, browses the site and puts an item into the basket.
That said your integration tests won't work if your routes don't work. Thus many say that integration tests is the place where you test routes.
However, considering the development cycle, you would create unit tests, controller tests, route tests, etc. first before doing the integration tests.
And on another note: assert_routing does both tests: assert_generates and assert_recognizes.

According to a comment in this rails bug, the proper place is in a functional test. If you try to test them in an integration test, the routes you established in routes.rb will not be available.
require 'test_helper'
class FooControllerTest < ActionController::TestCase
test "foo routes" do
assert_recognizes({:controller => 'foo_controller', :action => 'list'}, '/foos'
end
end
Route tests are good places to list links that exist in the wild, and avoid inadvertently breaking them with a code change. It's a little strange that the tests are scoped to a controller, since it's the route set as a whole you're actually testing, but I haven't heard of a 'route set test'.

If you are using rspec, then the most natural place for routing tests is in the directory spec/routing. From The Rspec documentation:
Routing specs are marked by :type => :routing or if you have set
config.infer_spec_type_from_file_location! by placing them in spec/routing.

Related

Mount additional routes in Rails specs

I want to test 3rd party sites embedding a javascript widget (provided by my Rails app) and submitting forms to an endpoint in my rails app.
To do this, I have created a dummy sinatra app that I test with Capybara:
# spec/system/widget_spec.rb
require 'sinatra/base'
class DummyApp < Sinatra::Base
get '/form' do
#widget_code = params[:widget_code]
erb :form
end
end
require 'rails_helper'
...
I'm defining this app before Rails is loaded in my spec, so that I can do the following in routes.rb:
mount DummyApp => '/dummy' if defined? DummyApp
This works if I run just this one spec, but if I run the whole suite, Rails is loaded before my DummyApp is defined.
Ideally I'd like to pull this line out of routes.rb entirely and allow my test to inject custom routes, so my routes file isn't cluttered with test-related routes.
1) Is there a way to tinker with rails routes within tests?
2) If not, what's the best way to go about testing this type of scenario?
There is a helper with_routing you could use (docs):
with_routing do |set|
set.draw do
mount DummyApp => '/dummy'
end
# ...
end

Enforce Single Rails Controller Action Invocation From An RSpec Example

Please bear with me while I give some background to my question:
I was recently integrating CanCan into our application and found that one of the controller rspec tests failed. Turns out this was a result of the test being poorly written and invoking an action on the controller twice.
it 'only assigns users for the organisation as #users' do
xhr :get, :users, { id: first_organisation.to_param}
expect(assigns(:users).count).to eq(3)
xhr :get, :users, { id: second_organisation.to_param}
expect(assigns(:users).count).to eq(4)
end
Note, the example is cut for brevity.
Now the reason this fails is because rspec is using the same controller instance for both action calls and CanCan only loads the organisation resource if it isn't already loaded.
I can accept the reasoning behind a) rspec using a single instance of the controller for the scope of the example and b) for CanCan to be only loading the resource if it doesn't exist.
The real issue here is that of course it's a bad idea to be invoking an action twice within the same example. Now the introduction of CanCan highlighted the error in this example, but I am now concerned there may be other controller tests which are also invoking actions twice or that such examples may be written in the future, which, rather long-windedly, leads me to my question:
Is it possible to enforce that a controller rspec example can only invoke a single controller action?
Ok, it does appear I have a solution:
Create unity_helper.rb to go into spec/support
module UnityExtension
class UnityException < StandardError
end
def check_unity
if #unity
raise UnityException, message: 'Example is not a unit test!'
end
#unity = true
end
end
RSpec.configure do |config|
config.before(:all, type: :controller) do
ApplicationController.prepend UnityExtension
ApplicationController.prepend_before_action :check_unity
end
end
then in rails_helper.rb
require 'support/unity_helper'
And this has actually highlighted another rspec controller example which is invoking a controller twice.
I am open to other solutions or improvements to mine.

Where to place helper specs (specs that test additional things in a Rails app)

Not to be confused by the title, I am not asking where I can place specs that test Rails helpers, but helpers specs that test additional things in a Rails app. For example, a spec that checks whether all translations are in place. Or that FactoryGirl factories are valid.
I know the guys at FactoryGirl recommend putting this check on RSpec's before :suite, but I prefer to leave it aside in a spec file of its own (so that it doesn't get executed when I want to run a single spec, for example).
So, what's the place to put these kind of specs?
I put FactoryGirl's specs in spec/models/*_spec.rb. For example
# spec/models/user_spec.rb
describe User do
describe "factories" do
it { expect(build(:user)).to be_valid }
end
end
For other kinds of specs, say for service objects, I put them in spec/services/*_spec.rb. RSpec will automatically detect them.
Edited
For translations, I test them in view specs. Firstly, set config to raise error on translations are missing.
# config/environments/test.rb
# Raises error for missing translations
config.action_view.raise_on_missing_translations = true
Then in the view spec,
describe "pages/welcome" do
it { expect { render }.not_to raise_error }
end

What is Shoulda's route matcher for?

According to the shoulda page,
ActionController Matchers
- route tests your routes.
And I'm just not sure what it's used for. Especially in an isolated controller unit spec. Why is this in a controller spec? What's the relevance? Shouldn't this simply be in its own routes spec? And yet on the shoulda page this matcher is listed as an ActionController Matcher...
Or is ActionController in charge of routes so uses this matcher?
If I should be using this matcher in my controller specs, could I have an example to show how should I go about using this?
Why is shoulda-matchers' route matcher an ActionController matcher?
shoulda-matchers' route matcher is an ActionController matcher simply because its subject can be a controller. You can specify :controller yourself, in which case the subject doesn't matter:
describe 'Routing' do
it { is_expected.to route(:get, '/posts/1').to(controller: :posts, action: :show, id: 1) }
end
or you can let it infer :controller for you:
describe PostsController do
it { is_expected.to route(:get, '/posts/1').to(action: :show, id: 1) }
end
In the latter case, all route does with the subject (PostsController) is call .controller_name on it. But it uses the controller, so it's an ActionController matcher.
Is route for controller specs or routing specs?
Routing is a completely separate layer of Rails from controllers, so RSpec has separate controller and routing specs, and that's how I write them. But I do use shoulda-matchers, and I like to not have to type controller:, so I make the subjects of my routing specs controllers as in the second example above.
If you write your routing specs that way the subjects of your controller and routing specs are the same, so you might find it convenient to put your routing examples in your controller spec files. It would make it easier to keep your controller and routing specs in sync as you add or remove controller actions. I wouldn't argue against that. I would keep in mind, though, that the controller isn't really the subject of the routing specs; it's just saving you typing.
The shoulda-matchers rdoc suggests both possibilities.

Writing functional tests for facebooker controller?

Anyone have any tips for best practices for mocking out facebook requests in functional tests? Is it just as simple as adding all of the proper params to the request? Is there a way to stub those out?
I'm using facebooker, which comes with a mock service:
# A mock service that reads the Facebook response from fixtures
# Adapted from http://gist.github.com/44344
#
# Facebooker::MockService.fixture_path = 'path/to/dir'
# Facebooker::Session.current = Facebooker::MockSession.create
But when I write a basic get test, it tries to redirect the browser to the facebook page for adding the app, which I assume indicates that the mocking isn't working.
test "loads respondent" do
Facebooker::Session.current = Facebooker::MockSession.create
get :index
puts #response.body # => <html><body>You are being redirected.</body></html>
end
I got this working with the latest version of facebooker (1.0.58):
# test_helper.rb
require 'facebooker/mock/session'
require 'facebooker/mock/service'
Facebooker::MockService.fixture_path = File.join(RAILS_ROOT, 'test', 'fixtures', 'facebook')
Obviously you will have to create the facebook directory in fixtures, or put it wherever. Inside you have to add a folder for each facebook method, and an xml file for the different types of responses you want to test for. I had to add facebook.users.getInfo and facebook.users.hasAppPermission. The easiest is just to add a file named default.xml with the example code from the facebook wiki for those actions.
# Controller test
test "facebook action" do
get :index, {:fb_sig_added => true}, :facebook_session => Facebooker::MockSession.create
assert_response :success
end
The fb_sig_added param is necessary as far as I can tell, because the internal facebooker logic checks the params directly before checking the session on that one. Which seems a bit wanky to me but maybe there's a reason for that.

Resources