I am somewhat new to testing in rails, and I'm wondering if this is a sufficient test for my new controller.
test "should get new" do
get :new
assert_response :success
end
Controller:
def new
#question = Question.new
end
Because the new action stores the controller in memory and does not write it to the DB, nor does it validate it. This seems sufficient to me. Any thoughts?
Controller testing should generally assert a few things
Controller rendered the correct template
You redirected to the right place
The instance variable has the correct data
Sometimes I also send some extra post variables in to make sure someone isn't goin to be able to curl themselves into an admin.
Related
I have a rspec test testing a controller action.
class SalesController < ApplicationController
def create
# This redirect sends the user to SalesController#go_to_home
redirect_to '/go_to_home'
end
def go_to_home
redirect_to '/'
end
end
My controller test looks like
RSpec.describe SalesController, type: :controller do
include PathsHelper
describe 'POST create' do
post :create
expect(response).to redirect_to '/'
end
end
However, when I run the test it tells me that:
Expected response to be a redirect to <http://test.host/> but was a redirect to <http://test.host/go_to_home>.
Expected "http://test.host/" to be === "http://test.host/go_to_home".
/go_to_home will send the user to SalesController#go_to_home. How can I test that the response will eventually lead to the home page with the url http://test.host/?
Why are you expecting to be redirect to '/' in the specs?
From the controller code you pasted you are going to be redirected to '/go_to_home' after hitting the create action
Try changing the specs to:
expect(response).to redirect_to '/go_to_home'
Edit:
Is this a real example or the code is just for sharing what you are trying to achieve?
I don't think rspec will follow the redirect after going to '/go_to_home' and I think is fine.
If you are testing the create action it's ok to test that redirects to '/go_to_home' because that's what action is doing.
Then you can do another test for the other action go_to_home and expect that redirects to root.
Are you calling the action 'go_to_home' from somewhere else?
Controller tests are effectively unit tests - you are testing the effect of calling a single action and what the expected behaviour of that action is.
The create action does return a response back with a status code of 302 and includes in the header a Location indicating the new URI, which in the case of calling create would be Location: http://localhost/go_to_home
This is as far as the controller test goes. It has emulated a call made from a browser to the create action and received the initial redirection.
In the real world of course the browser would then navigate to the given location and would then hit the go_to_home action but this is beyond the scope of controller tests ... this is in the domain of integration testing.
So, either,
Create an integration test to initially call the create action, follow the redirection and test that you end up at '/'.
Change the controller test to expect(response).to redirect_to '/go_to_home'
Change the create action to redirect directly to '/'
I was looking into some rspec testing lately and I was wondering how to properly test controllers. My controller is fairly simple so it shouldn't be something too hard:
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /users
def index
#q = User.search(params[:q])
#users = #q.result(distinct: true)
#q.build_condition if #q.conditions.empty?
#q.build_sort if #q.sorts.empty?
end
# GET /users/1
def show
end
# GET /users/new
def new
#user = User.new
end
# GET /users/1/edit
def edit
end
def archive
#q = User.search(params[:q])
#users = #q.result(distinct: true)
#q.build_condition if #q.conditions.empty?
#q.build_sort if #q.sorts.empty?
end
# POST /users
def create
#user = User.new(user_params)
if #user.save
redirect_to users_path, notice: 'Student was successfully added.'
else
render action: 'new'
end
end
# PATCH/PUT /users/1
def update
if #user.update(user_params)
redirect_to #user, notice: 'Student information was successfully updated.'
else
render action: 'edit'
end
end
# DELETE /users/1
def destroy
#user.destroy
redirect_to users_url, notice: 'Student information was successfully deleted.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def user_params
params.require(:user).permit(:firstName, :lastName, :email, :dateOfBirth, :notes, :sex, :archive, :category => [])
end
end
So far I have written 2-3 tests but I am not sure if they even do anything:
describe 'GET #index' do
it "displays all users" do
get :index
response.should be_redirect
end
end
describe 'GET #new' do
it "creates a new user" do
get :new
response.should be_redirect
end
end
I tried doing the same for edit and show but they didn't work and I am not sure why (because as I said, I don't know what I am doing).
Could anyone give me a few test examples for these methods or could redirect me to an rspec guide for rails4?
Are you expecting the controller #index action to redirect? Because that wouldn't be typical.
I would
describe 'GET #index' do
get 'index'
it {expect(response).to be_success)}
end
This line...
it "displays all users" do
in a controller spec makes me wonder if your confusing controller and request specs. I did this when I first got running with testing. "Displaying all users" sounds like a request spec to me. Testing if a page redirects or response status codes is more akin to controller specs.
I found http://betterspecs.org/ to be a really helpful resource in understanding testing better.
RE: WHAT to test
This worked for me but results may vary.
Controller Specs - Don't test controllers
Controllers should be skinny so you're just testing whether Rails is working. e.g. an index action may contain #users = User.all or similar and very little else. What is there to test there? Nothing. If you have lots of code in your controller actions then it probably shouldn't be there. Move it out to the models. Remember: Fat models, skinny controllers. This is an example of how testing creates better code. I have very few controller specs and I think nearly all of them are double checking authorisation to pages. I only use them where there's code in the controller. Here's an example:
context "Non admin signed in" do
before(:each) do
sign_in user
controller.stub!(:current_user).and_return(user)
end
it {subject.current_user.should_not be_nil}
it "deny non admin access to index" do
sign_in user
get 'index'
expect(response).to render_template("pages/access_denied")
end
end
Request Specs Test what you would test in a browser (20% of tests)
Imagine that you weren't doing RSpec testing. If you're like me then this is not too hard to imagine. How would you test the thing you want to build? Chances are that the first thing you'd do is load up a browser and see if something is on the page that you were expecting. That IS a request spec. It's that simple. Request specs are the automated ways of loading up a browser, clicking on a few buttons and checking what happened. Whatever it is your checking in the browser... check that same thing using Capybara. If it has Javascript on the page then you'll need Webkit or Selenium on top of Capybara to push the buttons as you would. With selenium you actually see the browser window pop up on the desktop as if a mysterious gremlin had taken control of your keyboard. Don't test anything in a request spec that you wouldn't be testing manually in a browser. That means don't check the state of other models in the database. Request specs are what the user can see. If you can't see it, don't test it.
Model specs - Test what you would test in the console (80% of tests)
Before I became a good TDD/BDD boy I found I spent a lot of time loading up irb or console and making models and doing X to see if Y would happen. Automate that thing. That's a model spec. When your request spec fails (which it should at first if it's doing anything useful) then drop down into the model spec. A failing request spec might be:
it {expect(page.find('#login_box')).to have_content 'Logged in as Kevin Monk'}
from
no method full_name for instance of User
And if you weren't a TDD good boy you might load up the console and find what was happening with the full_name method.
> rails console
$> kevin = User.find(1)
$> kevin.full_name
And then visually check that you get the full name baack but this should be done as a model spec.
I hope that helps. A problem I've had with a lot of books on testing is that the authors tend to be such experts and therefore don't appreciate that us mortals really need to understand the basic premise of what it is your supposed to be testing.
you have a typo in your spec code , you have to change respone, for response
I think that´s the problem
you can find more information in about test controllers in
https://www.relishapp.com/rspec/rspec-rails/docs/controller-specs
regards
I'm new to Ruby on Rails world.
I noticed there is at least one way to access controller instance variables from a test case.
Indeed, suppose this test method:
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:products)
end
products is an instance variable contained within concerned controller. And for sure, test case has reference to this controller. So assigns() method uses it to inspect a hash of controller's instance variables and thus allows to access any precised object from any other files that previously called an action to this controller.
So I wonder two questions:
Why do not create a 'Binding' to controller instead of using assigns() method?
I imagine a version where it is possible to do:
test "should get index" do
get :index
assert_response :success
assert_not_nil #products
end
Wouldn't it be shorter and cleaner?
Binding is the mechanism that allows ERB file to access controller instance variables, as shows this links:
http://rrn.dk/rubys-erb-templating-system
What isn't this mechanism applicable to Test case ? Is assigns() method essential ?
If you brought over the binding, though, this might pass and should not
test "should get index" do
#fake_products = [1,2,3]
get :index
assert_response :success
assert_not_nil #fake_products
end
You wouldn't necessarily want all instance variables in your test to combine with instance variables in your controller. Assigns lets you 'scope' your assertions to just the controller instance variables.
I'm trying to switch from using respond_to to respond_with in Rails controllers. Everything's going smoothly, except for testing invalid saves in controller specs. Here's an example:
describe MyController do
...
describe "PUT update" do
context "with invalid attributes" do
it "should re-render the edit page" do
style = stub_model(Style)
Style.stub(:find) { style }
Style.any_instance.stub(:save).and_return(false)
put :update
response.should render_template(:edit)
end
end
end
end
This works just fine with my old respond_to style update action, but with respond_with, I get
Failure/Error: response.should render_template("edit")
So, in short - how do I test this? ...Or should I just assume render_with knows what it's doing and not test at all? Any general suggestions?
Cheers in advance
PS: The update action:
def update
#style = Style.find(params[:id])
flash[:notice] = "Style updated" if #style.update_attributes(params[:style])
respond_with(#style)
end
I've been looking into this exact thing (how I found this topic) - so far I have the following:
Location.any_instance.stub(:valid?).and_return(false)
Location.any_instance.stub(:errors).and_return('anything')
(where Location is my model that uses respond_with)
however I believe there must be a nicer way to do it - if I find it, I'll be sure to post it!
Note: I'm also using the responders gem so one of these lines may not be necessary for you if you're not using it!
I have the following RSpec example which is passing:
describe UsersController do
it "should render new template on new action" do
get :new
response.should render_template("users/new")
end
end
The problem is I have not implemented the 'new' action on the UsersController.
Can anyone tell me why this test is passing?
I am new to RSpec, so any tips would be greatly appreciated!
When requesting an action for which a view exists, but the action is not defined, Rails will simply render the view. Therefore your spec (correctly) passes.
In addition to this spec, you may want to test for the assignment of particular instance variables. Example:
it "should assign the found articles for the view" do
assigns[:article].should == [#article]
end