How can I test common Rails controller behavior while staying DRY? - ruby-on-rails

I've been writing RSpec tests for some Rails controllers and I've discovered a strong impulse to ensure that the Authlogic authentication is working properly. I also feel like I should be verifying that each action uses the same application-wide layout. However, writing tests for this behavior in every single action seems silly.
What I'd like to see are one-line matchers for filters and layouts, similar to Shoulda's matchers for associations and verifications. Unfortunately, no such matchers seem to be available (except for some Test::Unit macros for filters in this blog post). I'm tempted to just write them myself, but not being able to find anyone who's already done it makes me question whether or not a need for such matchers actually exists.
So my question is, how do you test your controllers' common behavior (if you test it at all), and would one-liner matchers testing filters and layouts be useful? Myself, I'm trying to decide between one-liners in the controller specs combined with speccing the filter explicitly, or just speccing the filter and ignoring the filters and layouts in the controllers (since they're only one line of code anyway).

I don't like the idea of writing specs for filters -- that seems too close to the implementation. If you had used TDD/BDD methods to build your controller from scratch, presumably you'd have written the action first, added some logic (e.g. to handle authentication) and then realized it should go into a filter instead. If your spec is along the lines of "Reject an index request if the current user is not the account user", your spec ought to be able to do something like (aircode):
current_user = Factory.create(:unauthorized)
controller.should_not receive(:index)
get :index
request.should redirect_to(some_safe_path)
And it doesn't matter whether the action is using a filter or not.
You can DRY up controller specs with Rspec macros. So (more hand-waving):
describe MyController do
should_reject_anonymous(self)
...
end
module ControllerMacros
def should_reject_anonymous(test_controller)
describe test_controller, "Authentication" do
it "rejects index" do
test_controller.should_not_receive(:index)
get :index
response.should redirect_to(some_safe_path)
end
end
end
end

Related

Rspec multiple controllers with one common authentication

Hello guys i have a lot of controller test for an API in different files inside spec/controller folder. For each of those specs i have a
before do
authenticate_user(user, role)
end
This is specĀ“s time consuming because i have to create a User and a Role for every test. I would like to have this same helper at an upper level in order to execute all my controllers test under the same authentication.
does anyone has an idea of who to achieve this?
Stubing this is also a choice but there is a lot of logic under the user that may be difficult and time consuming.
thanks
You can use before(:suite) or before(:all) to prevent multiple calls to this method, e.g.
before(:suite) do
authenticate_user(user, role)
end
Also you can locate it at spec/support/sign_in_helper.rb, like:
module SignInHelper
def sign_in
before(:suite) do
authenticate_user(user, role)
end
end
end
and use it at spec_helper, like:
require 'spec/support/sign_in_helper'
extend SignInHelper
After that you'll be able to use it like macros in your controller specs at top-level.

Ruby MiniTest test design advice

I am testing http/https content for images that are on a web application running Rails 2.3. The layout is basic. I have my controllers with accompanying test directories including functional and integration folders with tests.
I'm still relatively new to Rails but I was wondering if there was a way to create a file that can test attachments across different controllers. For example, I need to test if whether the images on this web app are either http or https on the About/Projects/People/Accounts/ and Explore controllers. Instead of opening each of the about_controller_test, project_controller_test, etc. files writing the lines of code, I wanted to know if there was a way that I can make a master file that includes all of the controllers.
What I would like to do is to create some sort of module that I can include/extend the controllers in. This makes sense in my head but I run into the problem with naming conventions. If I wanted to make an attachments_test.rb, I wouldn't be able to do this because I don't have an attachments_controller.rb file that maps to it. How can I work around this?
All I would like is to make a file named along the lines of testing_attachment_protocols_test.rb which doesn't map to any controller but where I can put my tests in. I want to have one file to write my tests for different controllers instead of writing 1 test in several files. Would this file be included into test_helper.rb? Any help would be appreciated. Thanks.
---------EDIT-----------------------
I figured out the structure that I basically would like to implement for the test. Below is the general structure of the test that I would like to do.
require File.dirname(__FILE__) + '/../test_helper'
require 'open-uri'
def controller
......
end
class controller; def rescue_action(e) raise e end: end
class controller < Test::Unit::TestCase
def setup
#controller = controller
#request = ActionController::TestRequest.new
#response = ActionController::TestResponse.new
end
test "attachments url pattern for home page should include http when not logged in" do
setup
get :home
assert not_logged_in
#puts #response.body
assert_select("a img", :url =~ /sw.amazonaws/) do |anchor|
assert_equal 'http', anchor.protocol
end
end
end
Now the thing that I have trouble now is what to put in the method call for controller. My goal is to try to be as dynamic as possible. My goal is pretty much to create an attribute accessor method for different Controllers. Any thoughts?

How to access a controller constant in an rspec test under Rails 3

Using Rails 3.2 I have a controller in a subdirectory (e.g. /controllers/data_feeds/acme_feed_controller.rb)
This controller has some constants as below
class DataFeeds::AcmeFeedController < ApplicationController
MY_CONSTANT = "hi
def do_something do
...
end
end
In my rspec controller spec (which is in /spec/controllers/data_feeds/acme_feed_controller_spec.rb) I want to access that constant and below are two ways I've tried it (both commented out in the code below)
describe AcmeFeedController do
if "tests something" do
#c = AcmeFeedController.MY_CONSTANT
#c = DataFeeds::AcmeFeedController.MY_CONSTANT
end
end
I'm clearly not understanding something about the scope in which the spec test is run. What do I need to do and equally important why (i.e. what's happening with the scopes).
Thanks for your help.
Constants cannot be referenced with dot syntax, so DataFeeds::AcmeFeedController.MY_CONSTANT would never work in any context. You need to use :: to reference constants: DataFeeds::AcmeFeedController::MY_CONSTANT.
Note that is a ruby issue and has nothing to do with RSpec. When you face an issue like this, I recommend you figure out how to do it with plain ruby (e.g. in IRB) before worrying about how it works in RSpec (usually it will be the same, anyway).
If you want to know how constants work in ruby, I commend you watch this talk that explains them in detail.
Also, you can do this without repeating controller class name spaces.
describe AcmeFeedController do
if "tests something" do
c = controller.class.const_get('MY_CONSTANT')
end
end
This kind of trick may not be approved in application codes, but in tests it may be.

How to mock the redirect to an external URL for a integration/acceptance test?

In my Rails 3 application I have a controller with the following actions (code simplified):
def payment
redirect_to some_url_of_an_external_website
end
# the external website redirects the browser to this action when the payment is done
def payment_callback
#subscription = Subscription.new(:subscription_id => params[:subscription_id])
...
end
In my acceptance test (using steak and RSpec 2), I want to avoid the redirection to this external URL when capybara follows the link pointing to the payment action. Basically I want to mock the route helper payment_path so that it directly points to the payment_call_path with the appropriate subscription_id parameter.
Is this the correct way to do it? If so, how can I mock the payment_path (could not find how to do it)?
Whilst I usually try to avoid mocking in integration tests, here you can do something like this:
MyController.stub!(:payment).and_return('payment received').
Using class_eval as mentioned above will lead to that method being permanently stubbed out across your entire run (if you want this, I'd suggest stubbing it in spec_helper [that's assuming you use a spec_helper]). I find using rspec's mocking/stubbing stuff preferable anyway.
I'm not sure if this is the 'correct' way of doing this, but you can stub any of your application's code to return what you need for your test. So somewhere in your RSpec test you can do something like
MyController.class_eval do
def payment
'payment received'
end
end
Here is an example (see section 'Session Helper Methods') where the #admin? method in ApplicationController is stubbed when a custom RSpec helper module is included into the example group.

Use request helpers outside of controller specs

I'd like to include the request helpers (from ActionDispatch::Integration::RequestHelpers [ApiDock], like post and xhr methods) also in some specs outside of my controller specs. The problem is that these request helpers are only included in spec/controller and when a controller is described.
What do I have to include/require in those specs?
I am using RSpec 2 and Rails 3.
I just solved the problem by including the below code in my acceptance helper. If you are not Steak then just simply put it in spec helper or require it from somewhere else. post and xhr methods are now available in that spec regardless in what spec it is or in what directory you are.
The code is derived from RSpec::Rails::RequestExampleGroup
RSpec::Core::ExampleGroup.class_eval do
include ActiveSupport::Concern
include ActionDispatch::Integration::Runner
include RSpec::Rails::BrowserSimulators
def app
::Rails.application
end
def last_response
response
end
end
I know it's 4 years later and many things have of course changed, but since I stumbled on this question while searching how to make other tests behave like controller tests (and thus have post and get methods and the like) I wanted to point out this solution that works with RSpec 3: if you add this to the spec_helper
config.include RSpec::Rails::RequestExampleGroup, type: :request, example_group: { file_path: /spec\/(api|integration)/
it will make all tests in the given path have support for controller methods.

Resources