This is kind of a weird question so I'll probably be editing this to fill in some context as needed. I have an RSpec test:
it "should update object paramaters" do
put :update, params: {id: test_project, project: valid_params}
test_project.reload
expect(test_project.title).to eql valid_params[:title]
expect(test_project.created_at).to_not eql test_project.updated_at
end
Relevant controller action:
# #project is defined in a before_action
def update
if #project.update_attributes(project_params)
redirect_to #project, flash: { success: "Project successfully updated!" }
else
render :edit
end
end
This request returns a 200 OK (I would expect a redirect code, but this is not the case) response code, however when I reload test_project the title remains unchanged and the test fails. This EXACT same request works to update the Project in the development and production environments. Any clues as to why this isn't working?
I don't actually see a reload in the test (you should do one there). The 200 would suggest either (a) there's a user authentication step that you've not handled in your tests or (b) there's a validation issue. You can test for either / both by using gem 'pry' and binding.pry in your update method. If the pry doesn't execute in update, then update isn't executing (perhaps user authentication issue) and if it does execute, you can look at #project.errors.full_messages to see what the issue is.
Related
I'm trying to improve my test coverage (just installed simplecov) and was quite pleased to have good coverage already. However, I wasn't catching some failure modes in my tests, but I don't know how to force the failure of, for example, a save during creation of an object in Rails (5.2, if that matters). Any pointers on how to force and catch the failure of the save or update_attributes methods?
My main tests are working fine. I've just got no idea how to force the code down these branches in a test!
The relevant controller method:
def create
#project = current_user.projects.build(project_params)
if #project.save
flash[:notice] = "Project created!"
redirect_to user_path(current_user)
else
flash[:alert] = "There was an error. Your project was not created."
render 'new'
end
end
The first branch of this test (if #project.save) is tested. The second (else) isn't. The test that I'm using to test success is as follows:
sign_in #user
assert_difference 'Project.count', 1 do
post projects_path, params: {project: {name: #new_project_name, user_id: #user.id}}
end
assert_redirected_to user_path(#user)
What I'd really like is something where I can assert_no_difference successfully!
save returns false when there's a validation error.
Look at your Project model and produce parameters that are not valid. For example, if there's a validates :some_field, presence: true then skip that field and validation will fail.
After updating to Rails 5.0, I'm getting the following error:
"AbstractController::DoubleRenderError in RegistrationsController#create
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 "reditect_to(...) and return"."
This is my code, which worked before my update:
def create
# save record
if params[:stuff].nil?
respond_to do |format|
format.js
end
else
redirect_to root_path
end
end
I've tried a lot of different syntaxes, for example:
redirect_to(root_path) and return
redirect_to(root_path)
return
return and redirect_to(root_path)
return redirect_to(root_path)
But everything returns the same error. Anyone know the proper syntax?
You probably have render or redirect where you're showing the # save-record.
Try this:
Add gem byebug to your Gemfile if you don't already have it installed, run bundle to install it, and restart Rails
Add byebug to the start of your create method
Invoke create from the browser or command-line, and step through it with n and s to step into other functions. You'll probably notice render or redirect is being called twice
You can use performed? to test for, or debug, a double render/redirect.
I'm really stuck here. I have a Language model, that gets updated in this method:
def update
#language = Language.find(params[:id])
if #language.update_attributes(language_params)
flash[:success] = 'Language information updated.'
redirect_to #language
else
#skill_errors = #language.errors
render 'edit'
end
end
The intended behaviour for a successful update reproduces when I run it on my local server, object gets updated, flash appears and redirect to #language happens.
In the test, however, I only get the 200: Success response, and the object doesn't get updated. Here's the test code:
test 'should allow update when logged in as admin user' do
sign_in(#admin)
patch language_path(#ruby_language), params: { language: { name: 'Test'} }
assert_not flash.empty?
assert_redirected_to #ruby_language
#ruby_language.reload
assert_equal 'Test', #ruby_language.name
end
#admin and #ruby_language are defined in fixtures. All the asserts in this test fail, including the last one, with reload. My guess is that there might be some routing glitch caused by my usage of Devise and/or Kaminari gems? On the other hand, my Language routes are very simple:
resources :languages, concerns: :paginatable (the concern is here for human-readable URL formatting). Please, keep in mind that everything works as intended, only tests fail for some reason... Thanks in advance!
It turned out I was simply missing a required parameter in my update hash. Used Arun Kumar's tip of adding puts #response.body after patch.
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 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!