I'm trying to write an rspec test for a custom redirect page in Rails. Basically I've got a wildcard match in routes.rb that gets a page name, and a show method in a "Pages" controller that checks if a partial by that name exists. If it doesn't find a matching partial, it renders a 404 page and sets the status to 404. All this works in the browser, however rspec sees it as a 200 "OK" response, not a 404.
The code:
Pages controller show method (partial_exists? is a private method that I've thoroughly tested, and I can verify using the test logs that _missing.html.erb is being rendered as expected when rspec runs)
def show
#page_name = params[:page_name].to_s.gsub(/\W/,'')
unless partial_exists?(#page_name)
render :partial => 'missing', :status => :not_found
end
end
routes.rb:
match '/' => 'pages#show', :page_name => 'index'
match '*page_name' => 'pages#show'
spec:
require 'spec_helper'
describe PagesController do
render_views
describe "get page name of request" do
it "should generate http success for a defined partial" do
visit '/'
response.should be_success
end
it "should give respond with the 404 missing page for an undefined partial" do
visit '/blahblahblah'
response.status.should be(404)
end
end
end
The first test runs as expected, and renders _index.html.erb partial with a status code of 200. The second test renders _missing.html.erb (I've verified this by looking at the test logs), but reports a status code of 200, not 404 as expected. When I run it in the browser, it renders _missing.html.erb with a status code 404.
Any ideas?
It appears that you are confusing request (feature) specs with controller specs. visit is for use in request specs. For a controller spec, you want something like this:
describe "get page name of request" do
it "should generate http success for a defined partial" do
get :index
response.should be_success
end
end
Use get or post along with the appropriate action name to test the corresponding action in the controller.
Related
Im trying to set up a (basic) test for a new feature I am going to implement. I have a job controller and instead of default showing all jobs I like to hide all the ones which is archived. I tried different ways but it seems like i am missing a piece or two of this puzzle. First i tried with calling 'visit' but get the message it does not exist. Second approach is using 'render' but that also ends up in a error saying render does not exists. (can i even use these methods in a controller spec?)
Is it wrong to put this in a controller test?
2 last test are causing errors
require "rails_helper"
require "spec_helper"
describe JobsController, :type => :controller do
context 'GET index' do
it "should be successful" do
get :index
expect(response).to be_success
end
it "renders the index template" do
get :index
expect(response).to render_template("index")
end
it "should not show any archived jobs as default" do
visit jobs_path
page.should have_no_content("Archived")
end
it 'should show the headers' do
render :template => 'job/index', :layout => 'layouts/job'
rendered.should_not contain('Archived')
end
end
end
Capybara is used for feature specs, and its matchers can be used in view specs.
Controller specs, by default, don't actually render the view because they're the wrong place to be checking for page content - https://www.relishapp.com/rspec/rspec-rails/docs/controller-specs
You should probably move some of your tests to feature tests/view tests
I'm trying to set up specs to properly run with my nested resource.
This is the test code I'm trying to properly set up
it "redirects to the created unit" do
post :create, {:course_id => #course.id , :unit => valid_attributes}
response.should redirect_to(course_unit_path(#course, Unit.last))
end
That essentially should try to create a nested resource "unit" for "course".
Unfortunatly I'm getting the following error on all POST DELETE and PUT tests
Failure/Error: post :create, {:course_id => #course.id , :unit => valid_attributes}
NoMethodError:
undefined method `unit_url' for #<UnitsController:0x000000059f1000>
That makes sense since unit_url should be course_unit_url but it's RSpec calling it...
How can I make RSpec select the right named path?
For all GET tests I passed the :course_id by hand.
This is what I did:
it "redirects to the created unit" do
unit_id = "barry"
Unit.any_instance.should_receive(:save).and_return(true)
Unit.any_instance.stub(:id).and_return(unit_id)
post :create, {:course_id => #course.to_param , :unit => valid_attributes}
response.should redirect_to(course_unit_path(#course, unit_id))
end
I decided that the point of this test was not that it created a new model and redirected it, but simply that it redirects. I have another spec to ensure it creates a new model. Another benefit to this approach is that it doesn't touch the database so it should run a little faster.
I hope that helps.
Edit:
I also just noticed I have this in my before :each section which may be relevant:
Course.stub!(:find).and_return(#course)
Edit again:
In this case, there was code in the controller which was doing the offending call. As per comment below.
I want to test that my edit recipe page renders using rspec, though it doesn’t route to
recipes/edit
it routes to recipes/id/edit (id being replaced with a number)
my current test looks like this
describe "Show Edit Recipe Page" do
it "should display edit recipe page" do
get :edit
response.should be_success
response.should render_template(:edit)
end
end
how can i test this page correctly, at the moment my tests are failing
Problem
Your example doesn't include the code needed to actually test a controller object. RecipeController is not defined in your spec.
Solution
Make sure your controller specs live under spec/controllers or have an explicit type: :controller set. Then, actually describe a controller, either using the implicit subject or by setting up a controller instance in a before or test block. As the most basic example:
describe RecipeController do
# test something using the implied RecipeController.new
end
More Reading
RSpec Controller Specs
The get needs the id of the recipe passed in the params hash:
let(:recipe) { Factory.create(:recipe) }
it "should display edit recipe page" do
get :edit, :id => recipe.id
response.should be_success
response.should render_template(:edit)
end
I'm trying to move to rspec for testing, but I can't get controller testing to work with user authentication. Testing a route that doesn't require a user works.
require 'spec_helper'
describe UddsController do
include Devise::TestHelpers
render_views
before (:each) do
#user = User.new(:email => "test#user.com", :username => "test123")
#user.roles << Role.find_or_create_by_name("admin")
#user.save
sign_in #user
end
it "should get index" do
get :index
response.should be_success
end
I just get
2) UddsController should get index
Failure/Error: response.should be_success
expected success? to return true, got false
# ./spec/controllers/udds_controller_spec.rb:21
I'm not using factory_girl. All the example fixes here and on google seem to. I don't see why what I've done wouldn't work. Error is useless in debugging.
I know this question has been asked to death. I've read every blog and forum I can find. I'm at a dead end.
It might be redirecting. be_success returns false for any non-2xx status codes (like 302). Try checking the value of response.status -- if it's redirecting, it probably means your authentication or authorization scheme is producing an unexpected result.
Try adding puts response.body before your assertion, and see what the page you are requesting says. If you can't figure it out from there, perhaps edit your question to include the output?
If you are using devise's "confirmable" module which makes users click a link in an email to confirm their address, then when you use "sign_in" in the tests you also have to fake email confirming. It looks something like this:
sign_in #user
#user.confim!
I'm working on a Rails3 app that has a Pages controller, and two pages: pages#main and pages#status. The main page has a link which, when clicked, goes to status. The user already has profile information, and if part of that profile information is not present, I want status to redirect to main. I'm getting a mysterious double-redirect though, that I can't solve. Here's the pages controller:
def main
#current_user = current_user
end
def status
#current_user = current_user
if #current_user.address.blank?
redirect_to :action => "main" and return
end
end
Everything works swimmingly as long as the condition isn't met, but as soon as it is, I get the error:
Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".
Indeed, redirect_to is called twice (same line, according to the trace; not sure why). I'm wondering whether it is a routes problem. Here is routes.rb:
match '/main', :to => 'pages#main'
match '/status', :to => 'pages#status'
Any suggestions would be greatly appreciated.
Here is the development log:
Started GET "/status" for 127.0.0.1 at Wed Nov 03 21:28:15 -0700 2010
Processing by PagesController#status as HTML
User Load (0.3ms) SELECT "users".* FROM "users" WHERE ("users"."id" = 3) LIMIT 1
Before redirect
Before redirect
Redirected to
Redirected to
Completed in 32ms
AbstractController::DoubleRenderError (Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".):
app/controllers/pages_controller.rb:15:in `status'
app/controllers/pages_controller.rb:15:in `status'
Trying to solve the problem, I've added two debugging lines to the status method, in the hopes that it will provide some clue:
logger.debug "Before redirect"
redirect_to :action => "main" and return
logger.debug "After redirect"
So, the "Before redirect" lines are hit before we get to "Redirected to". Then "Redirected to" appears twice with no target. Incidentally, this happens with or without "and return" on the redirect line. I'm really not sure what's going on.
Also, interestingly, I added a debug line to the main method. It is never triggered.
Rav, can you paste the development log for this request? Specifically, does the first request for /status result in a redirect to /main, or does it fail with the above error?
Specifically, look in log/development.log for the most recent request block or blocks.