I have referred various answers on StackOverflow but I still haven't been able to resolve this issue. I am sharing the code for my controllers_test.rb and along with that the screenshot. Every time I run rails test, the following error is shown. I have made sure that console is close. In case there is any other source code needs to be share please do let me know.
require "test_helper"
class CategoriesControllerTest < ActionDispatch::IntegrationTest
setup do
#category = Category.create(name: "Sports")
end
test "should get index" do
get categories_url
assert_response :success
end
test "should get new" do
get new_category_url
assert_response :success
end
test "should create category" do
assert_difference('Category.count') do
post categories_url, params: { category: { name: "Travel" } }
end
assert_redirected_to category_url(Category.last)
end
test "should show category" do
get category_url(#category)
assert_response :success
end
end
test_helper.rb
class ActiveSupport::TestCase
# Run tests in parallel with specified workers
parallelize(workers: :number_of_processors, with: :threads)
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
# Add more helper methods to be used by all tests here...
end
Book: Agile Web Development with rails 5.1
I am running a simple code: bin/rails test on cli. The output is as follows; 0 failures - 7 errors.
I have run the code through - https://yamlchecker.com. I have no problems within the code. However, I continue to get the same errors when I run the bin/rails test. I am following the book example to learn ruby on rails.
require 'test_helper'
class ProductsControllerTest < ActionDispatch::IntegrationTest
setup do
#product = products(:one)
end
test "should get index" do
get products_url
assert_response :success
end
test "should get new" do
get new_product_url
assert_response :success
end
test "should create product" do
assert_difference('Product.count') do
post products_url, params:{
product:{
title: #product.title,
description: #product.description,
image_url: #product.image_url,
price: #product.price
}
}
end
assert_redirected_to product_url(Product.last)
end
test "should show product" do
get product_url(#product)
assert_response :success
end
test "should get edit" do
get edit_product_url(#product)
assert_response :success
end
test "should update product" do
patch product_url(#product), params:{
product:{
title: #product.title,
description: #product.description,
image_url: #product.image_url,
price: #product.price
}
}
assert_redirected_to product_url(#product)
end
test "should destroy product" do
assert_difference('Product.count', -1) do
delete product_url(#product)
end
assert_redirected_to products_url
end
end
Command Line Errors Messages
Top part of code - sublime text 3
Bottom part of code - sublime text
I am looking for 0 failures and 0 errors. Actual results: 0 failures and 7 errors. all from this file.
Try to not use tabulation on your yaml file. Instead use 4 spaces or check the configuration of your tab button.
I've got a filter which ensures that only super admins can access a particular action:
before_action :require_super_admin
def index; end
In my controller tests I've got:
test "should let super admin access index" do
login_super_admin
get :index
assert_response :success
end
test "should NOT let normal admin access index" do
login_normal_admin
get :index
assert_response :redirect
end
test "should NOT let user access index" do
login_user
get :index
assert_response :redirect
end
test "should NOT let guest access index" do
login_guest
get :index
assert_response :redirect
end
That's four tests to ensure that only a super admin can access the index. Is there a better way of testing this? Does anyone else ever find themselves doing this sort of thing? I run into it every time I build a rails app.
You can create shared examples
shared_example "allow super admins" do |actions|
actions.each do |action|
it "should let super admin access #{action}" do
login_super_admin
get action.to_sym
assert_response :success
end
end
end
shared example "deny non super admins" do |actions, users|
actions.each do |action|
users.each do |user|
it "should let not let #{user} access #{action}" do
send("login_#{user}")
get action.to_sym
assert_response :redirect
end
end
end
end
And in your tests that need authorization check, you can do
it_behaves_like "allow super admins", ["index"]
it behaves_like "deny non super admins", ["index"], ["normal_admin", "user", "guest"]
PS: I haven't tested this. This is just to give you an idea
My solution:
# Gemfile
gem 'shoulda-context'
# In test_helper.rb
class ActiveSupport::TestCase
def make_sure_authorization_kicks_in
assert_redirected_to root_url
assert_equal "You are not authorized to perform this action.", flash[:error]
end
end
# In controller tests
context "NOT Super Admin" do
# saves a lot of typing
teardown { make_sure_pundit_kicks_in }
context "just NORMAL ADMIN" do
setup { login_normal_admin }
should("NOT get index"){ get :index }
should("NOT get new"){ get :new }
end
context "just normal USER" do
setup { login_user }
should("NOT get index"){ get :index }
should("NOT get new"){ get :new }
end
end
This is much easier to manage.
So I don't have a great reason for needing to know this other than curiosity - the BEST reason - but I'm not sure what's going on here.
Background:
I'm working through the RSpec book and updating the examples.
On Chapter 24 - Rails Controllers there's a test for a messages controller.
## spec/controllers/messages_controller_spec.rb ##
require 'spec_helper'
describe MessagesController do
describe "POST create" do
let(:message) { mock_model(Message).as_null_object }
before do
Message.stub(:new).and_return(message)
end
# Then a bunch of Tests...
context "when the message fails to save" do
before do
message.stub(:save).and_return(false)
post :create
end
it "assigns #message" do
assigns[:message].should eq(message)
end
it "renders the new template" do
response.should render_template("new")
end
end
end
end
This goes along with the messages controller:
## app/controllers/messages_controller.rb ##
class MessagesController < ApplicationController
def create
#message = Message.new(params[:message])
if #message.save
flash[:notice] = "The message was saved successfully"
redirect_to action: "index"
else
render "new"
end
end
end
When I run the tests:
The test passes with response.
it "renders the new template" do
response.should render_template("new")
end
The test also passes with subject.
it "renders the new template" do
subject.should render_template("new")
end
The test Also passes with page
it "renders the new template" do
page.should render_template("new")
end
The test ALSO passes with NOTHING
it "renders the new template" do
should render_template("new")
end
In case it helps anyone make heads or tails of this, the config/routes.rb just has resources :messages
Why do all those tests pass? What am I actually testing? Are 'page', 'subject', and ' ' just synonyms for response?
Does it matter as long as my tests pass?
By default, the subject would be referencing the class, which is the MessagesController.
Not defining a subject in the last test example, will implicitly set the subject to be MessagesController.
From a binding.pry, it appears that subject is an instance of the controller class:
[2] pry(#<RSpec::ExampleGroups::MyController::DescribeString::ContextString>)> subject.is_a? Class
=> false
[3] pry(#<RSpec::ExampleGroups::MyController::DescribeString::ContextString>)> subject.is_a? Users::SessionsController
=> true
I'm working on a test for my Rails 4 app and I'm pretty new to using RSpec. I have a controller named AppsController which has the standard index, new, show, create... methods and they all work the way Rails suggest Etc. "new" creates a new instance of the object and create actually saves it, show, shows it and index shows all of the object. Here are my current tests can anyone see any potential problems or things that i could improve?
FactoryGirl.define do
factory :developer do
email 'example#me.com'
password 'new_york'
password_confirmation 'new_york'
tos '1'
end
factory :app do
name 'New App'
tos '1'
end
factory :invalid_app, parent: :app do
name 'nil'
tos '0'
end
end
require 'spec_helper'
def create_valid!
post :create, app: app_attributes
end
def create_invalid!
post :create, app: app_invalid_attributes
end
def show!
get :show, id: app
end
def update_valid!
put :update, id: app, app: app_attributes
end
def update_invalid!
put :update, id: app, app: app_invalid_attributes
end
def delete!
delete :destroy, id: app
end
def http_success
expect(response).to be_success
end
def expect_template(view)
expect(response).to render_template(view)
end
describe AppsController do
render_views
before(:each) do
#developer = FactoryGirl.create(:developer)
#developer.confirm!
sign_in #developer
end
let(:app) { FactoryGirl.create(:app, developer: #developer) }
let(:app_attributes) { FactoryGirl.attributes_for(:app) }
let(:app_invalid_attributes) { FactoryGirl.attributes_for(:invalid_app) }
describe 'GET #index' do
it 'responds with an HTTP 200 status' do
get :index
http_success
end
it 'renders the :index view' do
get :index
expect_template(:index)
end
it 'populates #apps with the current_developers apps' do
app = FactoryGirl.create(:app, :developer => #developer)
get :index
expect(assigns(:app)).to eq([app])
end
end
describe 'POST #create' do
context 'with valid parameters' do
it 'creates a new app' do
expect { create_valid!
}.to change(App, :count).by(1)
end
it 'redirects to the new app keys' do
create_valid!
expect(response).to redirect_to keys_app_path(App.last)
end
end
context 'with invalid parameters' do
it 'does not create the new app' do
expect { create_invalid!
}.to_not change(App, :count)
end
it 'renders the :new view' do
create_invalid!
expect_template(:new)
end
end
end
describe 'GET #show' do
it 'responds with an HTTP 200 status' do
show!
http_success
end
it 'renders the :show view' do
show!
expect_template(:show)
end
it 'populates #app with the requested app' do
show!
expect(assigns(:app)).to eq(app)
end
end
describe 'PUT #update' do
context 'with valid parameters' do
it 'locates the requested app' do
update_valid!
expect(assigns(:app)).to eq(app)
end
it 'changes app attributes' do
update_valid!
expect(app.name).to eq('Updated App')
end
it 'redirects to the updated app' do
update_valid!
expect(response).to redirect_to app
end
end
context 'with invalid parameters' do
it 'locates the requested app' do
update_invalid!
expect(assigns(:app)).to eq(app)
end
it 'does not change app attributes' do
update_invalid!
expect(app.name).to_not eq('Updated App')
end
it 'renders the :edit view' do
update_invalid!
expect_template(:edit)
end
end
end
describe 'DELETE #destroy' do
it 'deletes the app' do
expect { delete!
}.to change(App, :count).by(-1)
end
it 'redirects to apps#index' do
delete!
expect(response).to redirect_to apps_url
end
end
end
count should have been changed by -1, but was changed by 0 - on DELETE #destroy
expecting <"new"> but rendering with <[]> - on POST #create
expected: "Updated App"
got: "New App" - on PUT #update
expecting <"edit"> but rendering with <[]> - on PUT #update
expected: [#<App id: nil, unique_id: "rOIc5p", developer_id: 18, name: "New App">]
got: nil - on GET #index
Small thing - your #http_success method is testing the exact same thing twice.
You could also factor out the references to app by putting a #let statement right after your #before block:
let(:app) { FactoryGirl.create(:app, developer: #developer) }
then in your specs, just
it 'renders the :show view' do
get :show, id: app
expect_template(:show)
end
Edit:
Then the order of operations will be 1) the #developer is created in the #before block, 2) the spec is entered, 3) at the first reference to app in the spec, the #let block will create an instance of an app.
That means you can't factor out the app creation in the #index spec, because in that case the spec will call the action before it creates the app.
A few things I thought of, reading your code:
You don't need to include parens on method calls taking no arguments. Just http_success will work.
You should get try to use the modern RSpec expectation syntax consistently. Instead of assigns(:app).should eq(app), use expect(assigns(:app)).to eq(app). (There's one exception to this, which is expectations on mocks (ie. should_receive(:message)), which will only take on the modern expect-to syntax as of RSpec 3.
For controller specs, I like to create little methods for each action that actually invokes the action. You'll notice you call get :show, id: app several times in the GET #show specs. To DRY up your specs a little more, you could instead write the following method within the describe block:
def show!
get :show, id: app
end
Try to use one Hash syntax consistently. What's more, Rails 4 can't be run with Ruby 1.8, so there's (nearly) no reason to use the hash-rocket Hash syntax.
If I'm getting really, really picky, I usually consider instance variables in a spec to be a smell. In almost all cases, instance variables should be refactored to a memoized let/given blocks.
If I'm getting really, really, really picky, I prefer to think of controller specs such as yours as strictly a unit test of the controller, not an integration test (that's what Capybara is for), and as such you shouldn't be exercising your model layer at all. You should only be testing that your controller is sending the right messages to the model layer. In other words, all the model layer stuff should be stubbed out. For example:
describe 'GET #show' do
let(:app) { stub(:app) }
before do
App.stub(:find).and_return(app)
end
it 'populates #app' do
get :show, id: app
assigns(:app).should eq(app)
end
end
I know this last is a personal preference, not a metaphysical truth or even necessarily a wide-spread standard convention, so you may choose to take it or leave it. I prefer it, because it keeps my specs very speedy, and gives me a very clear heuristic for when my controller actions are doing too much, and I might need to consider refactoring. It could be a good habit to get into.
First, I'm not certain but I suspect your invalid app factory may be wrong. Did you mean
factory :invalid_app, parent: :app do
name nil
tos '0'
end
nil as a ruby NilClass not "nil" as a string?
As for other comments about cleanup and stuff, here are a few of my thoughts.
You can avoid the need for some of your helper methods and duplication by using before blocks for each describe. Taking just your index tests you could have something more like
describe 'GET #index' do
before do
get :index
end
it 'responds with an HTTP 200 status' do
http_success
end
it 'renders the :index view' do
expect_template(:index)
end
it 'populates #apps with the current_developers apps' do
expect(assigns(:app)).to eq([app])
end
end
Notice also that you don't need to recreate app because your let is doing it as necessary.
On the failures, I suspect the delete count change could be failing because inside the expectation, the test framework is creating a new app (from the let) and then deleting it leading to a count change of 0. For that test, you need to make sure you're app is created outside of your expectation. Because you are using let, you could do that like this:
describe 'DELETE #destroy' do
it 'deletes the app' do
# ensure that app is already created
app
expect {
delete!
}.to change(App, :count).by(-1)
end
end
alternatively, change the let to a let! which will force the creation before the specs actually run.
As for other failures, thought #DanielWright suggested the helper methods, I find those complicate the debug. I can't see where you set the app name to "Updated App", for example. Perhaps a clearer test (for that particular one) would not use the helper methods but could be more explicit. Something like
describe 'PUT #update' do
let(:app_attributes) { FactoryGirl.attributes_for(:app, name: 'The New App Name') }
before do
put :update, id: app, app: app_attributes
end
context 'with valid parameters' do
it 'locates the requested app' do
expect(assigns(:app)).to eq(app)
end
it 'changes app attributes' do
# notice the reload which will make sure you refetch this from the db
expect(app.reload.name).to eq('The New App Name')
end
it 'redirects to the updated app' do
expect(response).to redirect_to app
end
end
end
For the other errors, you might want to start debugging your code. Are you certain it should work? Have you looked at output logs? Maybe the tests are doing there job and finding errors in your controller code. Have you done any step-through debugging?