Learning RSpec 3+, and learning that in order to render the template from the controller, you have to write render_views within a example group like so:
describe CustomersController do
describe "GET INDEX" do
render_views
it "renders the index template"
expect(response.body).to match(/Customer List/)
end
end
end
Just wondering how the implementation of it, more specifically, how does it work behind the scenes. Is render_view a method, a variable? Just wondering how RSpec knows to render the views for that particular example group, if render_views is typed in?
You can see for yourself in the rspec-rails source code.
Related
I am looking for clarification and an understanding on how to effectively test my controllers with Rspec, I don't want to write tests that are not testing the potential issues at hand.
My scenario is as follows.
I am using Active Admin to create a Category, to do so you must obviously be logged into Active Admin.
What I want to ensure is that
1) A logged in user can create a Category
2) A Category cannot be created if you are not logged in
3) Attempts to create a Category outside of active admin are met with a 404 template
So what i have so far (and i really want to check i haven't gone over the top or performing unnecessary tests) is as follows.
spec/controllers/categories_controller_spec.rb
require 'rails_helper'
include Warden::Test::Helpers
# Ensure 404 pages are returned when requesting URLS
RSpec.describe CategoriesController, type: :request do
describe 'Routes' do
context 'All CRUD actions render 404' do
it '#create' do
post '/categories'
expect(response.status).to eq(404)
expect(response).to render_template(:file => "#{Rails.root}/public/404.html.erb")
end
# All other actions here
end
end
end
RSpec.describe Admin::CategoriesController, type: :request do
describe 'No Authorised Login' do
context 'All CRUD actions redirect correctly' do
it 'redirects when accessing #index' do
get '/admin/categories'
expect(response.status).to eq(302)
expect(response).to redirect_to(admin_root_path + '/login')
end
# All other actions here
end
end
end
# Ensure actions in admin can be carried out if logged in
RSpec.describe Admin::CategoriesController, type: :request do
before(:each) do
#user = FactoryGirl.create(:admin_user)
login_as #user
end
after(:each) do
#user.destroy
end
describe 'Authorised Login' do
context 'All CRUD actions perform as expected' do
it 'navigates to Categories #index' do
get '/my_admin_panel/categories'
expect(response.status).to eq(200)
expect(response).to render_template(:index)
end
# All other actions here
end
end
spec/routing/categories_routing.spec
RSpec.describe CategoriesController, type: :routing do
describe 'Routes' do
it 'does not get #index' do
expect(get: '/categories').to route_to(
controller: 'application',
action: 'raise_not_found',
unmatched_route: 'categories'
)
end
end
end
Should I be testing post /categories without supplying params, is that a wasted test? Am I over complicating what should be a simple set of tests ?
This is a judgement/style question and, as such, is not ideal for the StackOverflow format. That said, I don't think your testing is over the top. Some other thoughts:
Some people choose treat their controller tests as integration tests.
You can take advantage of RSpec's shared examples to DRY up your tests
In the default RSpec configuration, the database will be cleaned after each test, so you don't need to explicitly destroy the ActiveRecord objects you create if it's the database you're worried about
In general, I think testing behavior for programmatic actions you don't expect to happen (e.g. posting to undefined routes) is worthwhile unless you are trying to test specific error handling code
I have some controllers that are in subdomains of the controllers folder.
for example, i have a controller in app/controllers/api/v1/offers_controller.rb that looks like this:
class Api::V1::OffersController < ApplicationController
respond_to :json
def index
...some code here
end
end
I tried putting a controller in spec/controllers/api/v1/offers_controller.rb that looks like this:
require 'spec_helper'
descripe Api::V1::OffersController do
describe "GET 'index'" do
it "returns http success" do
get 'index'
response.should be_success
end
end
end
however, when I run rspec spec, this test does not get run at all. I also tried putting it in the spec/controllers directory, named api_v1_offers_controller.rb, and the test is still not run.
How can I write RSpec tests for these types of controllers?
It actually seems it was a mistake it how I named the file. it seems all RSpec tests need to end it _spec
I am trying to check the title of a page, but it is failing with:
rspec ./spec/controllers/pages_controller_spec.rb:13 # PagesController GET 'home' should have the right title
Here is what pages_controller_spec.erb looks like:
render_views
describe "GET 'home'" do
it "returns http success" do
get 'home'
response.should be_success
end
it "should have the right title" do
get 'home'
response.should have_selector("title",:content => "Home")
end
end
According to this, I think you need :text not :content:
http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Finders#all-instance_method
I hope that helps.
rspec controller tests stub the view so you can test the controller in isolation
https://www.relishapp.com/rspec/rspec-rails/v/2-12-2/docs/controller-specs/views-are-stubbed-by-default
if you check the response object it probably contains a blank string
the idea being you should test
if the correct template rendered
if the controller redirected correctly
if the controller called the correct models, etc...
that being said, if you want the test to render the view, use render_views in the describe block
https://www.relishapp.com/rspec/rspec-rails/v/2-2/docs/controller-specs/render-views
Building off of what house9 says, you should test the title of your views somewhere else, and not in your controller specs. For instance, you can create a "requests" directory within your spec directory and test page features there. For more information on request specs:
http://railscasts.com/episodes/257-request-specs-and-capybara?view=asciicast
I'm doing some tests here using Rspec and I would like to assure that the controller is calling the log method in some actions. I'm also using mocha.
I would like something like this:
it "update action should redirect when model is valid" do
Tag.any_instance.stubs(:valid?).returns(true)
put :update, :id => Tag.first
controller.expects(:add_team_log).at_least_once
response.should redirect_to(edit_admin_tag_url(assigns[:tag]))
end
is there something to use as the 'controller' variable? I tried self, the controller class name...
I just got helped with this. For testing controllers, you'd nest your specs inside a describe which names the controller. (The spec should also be in the Controllers folder)
describe ArticlesController do
integrate_views
describe "GET index" do
...
it "update action should redirect when model is valid" do
...
controller.expects(:add_team_log).at_least_once
...
end
end
end
I think you want #controller instead of controller. Here's an example from my test suite:
it "delegates to the pricing web service" do
isbn = "an_isbn"
#controller.expects(:lookup)
.with(isbn, anything)
.returns({asin: "an_asin"})
get :results, isbn: isbn
assert_response :success
end
I have an Ruby on Rails 3 admin_controller with the default set of CRUD, index and so on methods. I'd like to test each of these for certain assertions with rspec.
Like response.should render_template("layouts/some_layout") or tests that it should require login.
Copy-pasting that test into the group of tests for each method is a lot of duplication. IMO it makes little sense to have an
it 'should require login' do
Duplicated several times troughout that test.
Is there a simple way to run a test on a list of methods? Say defined_methods.each do |method| it 'should' .... of some sort?
Is this a good way in the first place? Or am I taking a wrong route in the first place?
Given that you really want all those assertions, have you considered shared example groups?
shared_examples_for "an action that requires authentication" do
it "should render successfuly" do
sign_in(user)
response.should be_success # or whatever
end
it "should deny access" do
# don't sign_in the user
# assert access was denied
end
end
shared_examples_for "another behaviour" do
# ...
end
let(:user) { create_user }
describe "#index" do
before(:each) { get :index }
it_behaves_like "an action that requires authentication"
it_behaves_like "another behaviour"
end
describe "#show" do
before(:each) { get :show }
it_behaves_like "an action that requires authentication"
end
# ...
Of course before writing large number of specs for a basic functionality you should always check if it isn't already tested by the library that is providing the functionality (e.g. checking for the rendered template, if it is handled by rails's implicit rendering, might be a bit overkill).
If you wanted to go down the route of iteratively testing each public method in the controller, you could do something like:
SomeController.public_instance_methods(false).each do |method|
it "should do something"
end
However, I think a shared example group (see about half way down this page: http://rspec.info/documentation/) would be prettier. If it were extracted so it could be used across all your controller specs, it'll be even nicer..
shared_examples_for "admin actions" do
it "should require login"
end
Then in each controller spec:
describe SomeController do
it_should_behave_like "admin actions"
end
Just add it to your test_helper.rb, something like:
def requires_login
...
end