RSpec tests pass in isolation, but fail when run together - ruby-on-rails

In my code_controller_spec.rb my tests pass when I just run the tests in code_controller_spec.rb. However, when I run the suite, only the create, edit, and update tests fail within code_controller_spec.rb. I'm really not sure where to go from here and I really don't know why my tests fail when running together. I have config.use_transactional_fixtures = false and am using database_cleaner. My app works fine locally with the below actions and I don't receive any errors when creating or updating a post. Let me know if you need additional info.
When I run the suite, the failures I get are:
1) CodesController admin pages #create posts a new code post
Failure/Error:
post :create, params: {
code: {
created_at: Date.today,
title: "Code things",
content: "The content of the code post",
user_id: user.id
}
}
ActionController::UrlGenerationError:
No route matches {:action=>"create", :code=>{:created_at=>Fri, 20 Oct 2017, :title=>"Code things", :content=>"The content of the code post", :user_id=>1}, :controller=>"codes"}
2) CodesController admin pages #edit edits a code post
Failure/Error: get :edit, params: { id: code }
ActionController::UrlGenerationError:
No route matches {:action=>"edit", :controller=>"codes", :id=>#<Code id: 1, title: "Code post title", content: "Coding speak that not everyone can understand...", created_at: "2017-10-20 00:00:00", updated_at: "2017-10-21 06:05:27", user_id: 2>}
3) CodesController admin pages #update updates a code post
Failure/Error: put :update, params: { id: code, code: code2 }
ActionController::UrlGenerationError:
No route matches {:action=>"update", :code=>{:title=>"Updated title", :content=>"Updated content"}, :controller=>"codes", :id=>#<Code id: 1, title: "Code post title", content: "Coding speak that not everyone can understand...", created_at: "2017-10-20 00:00:00", updated_at: "2017-10-21 06:05:27", user_id: 2>}
My spec/controllers/user/codes_controller_spec.rb
RSpec.describe User::CodesController, type: :controller do
let!(:user) { User.create(email: "user#example.com", password: "password") }
let!(:code) { FactoryGirl.create(:code) }
before do
sign_in_as user
expect(response).to have_http_status(:success)
end
describe "admin pages" do
render_views
context "#create" do
it "posts a new code post" do
post :create, params: {
code: {
created_at: Date.today,
title: "Code things",
content: "The content of the code post",
user_id: user.id
}
}
expect(response).to redirect_to user_codes_path
expect(flash[:success]).to eq "Post created successfully."
end
end
context "#edit" do
it "edits a code post" do
get :edit, params: { id: code }
expect(response).to render_template :edit
end
end
context "#update" do
let(:code2) do
{ title: "Updated title", content: "Updated content" }
end
it "updates a code post" do
put :update, params: { id: code, code: code2 }
code.reload
expect(response).to redirect_to user_code_path(code)
expect(code.title).to eq code2[:title]
expect(code.content).to eq code2[:content]
expect(flash[:success]).to eq "Post updated successfully."
end
end
end
end
My spec/factories/post_factories.rb
FactoryGirl.define do
factory :code do
created_at Date.today
title "Code post title"
content "Coding speak that not everyone can understand..."
association :user, factory: :user
end
factory :life do
created_at Date.today
title "Life post title"
content "The world moves in mysterious ways; whether we want it to..."
association :user, factory: :user
end
end
I run rspec --seed 123 --bisect and then I run the output of that, but all my tests pass. I should mention I'm very new to --bisect.
This is the rspec --seed 123 --bisect output and when I run this all my tests pass.
rspec ./spec/controllers/user/codes_controller_spec.rb[1:1:1:1,1:1:2:1,1:1:3:1,1:1:4:1,1:1:5:1] ./spec/controllers/user/lives_controller_spec.rb[1:1:1,1:1:2] ./spec/features/admin_create_posts_spec.rb[1:1,1:2,1:3,1:4] ./spec/features/admin_edit_post_spec.rb[1:1,1:2,1:3] ./spec/features/clearance/user_signs_out_spec.rb[1:1] ./spec/features/clearance/visitor_resets_password_spec.rb[1:1,1:2,1:3,1:4,1:5,1:6] ./spec/features/clearance/visitor_signs_in_spec.rb[1:1,1:2,1:3,1:4,1:5,1:6,1:7,1:8] ./spec/features/clearance/visitor_signs_up_spec.rb[1:1,1:2,1:3,1:4,1:5,1:6,1:7,1:8] ./spec/features/clearance/visitor_updates_password_spec.rb[1:1,1:2,1:3] ./spec/features/homepage_spec.rb[1:1,1:2] ./spec/features/login_users_spec.rb[1:1,1:2] ./spec/features/logout_user_spec.rb[1:1] ./spec/features/showing_all_posts_spec.rb[1:1,1:2] ./spec/helpers/codes_helper_spec.rb[1:1] ./spec/helpers/lives_helper_spec.rb[1:1] ./spec/helpers/user/codes_helper_spec.rb[1:1] ./spec/helpers/user/lives_helper_spec.rb[1:1] ./spec/helpers/users_helper_spec.rb[1:1] ./spec/helpers/welcome_helper_spec.rb[1:1] ./spec/models/code_spec.rb[1:1] ./spec/models/life_spec.rb[1:1]
My database_cleaner config in rails_helper.rb
require database_cleaner
RSpec.configure do |config|
config.use_transactional_fixtures = false
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
# config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.before(:all) do
FactoryGirl.reload
end
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.before(:suite) do
if config.use_transactional_fixtures?
raise(<<-MSG)
Delete line `config.use_transactional_fixtures = true` from rails_helper.rb (or set it to false) to prevent uncommitted transactions being used in JavaScript-dependent specs.
During testing, the app-under-test that the browser driver connects to uses a different database connection to the database connection used by the spec. The app's database connection would not be able to access uncommitted transaction data setup over the spec's database connection.
MSG
end
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, type: :feature) do
# :rack_test driver's Rack app under test shares database connection
# with the specs, so continue to use transaction strategy for speed.
driver_shares_db_connection_with_specs = Capybara.current_driver == :rack_test
if !driver_shares_db_connection_with_specs
# Driver is probably for an external browser with an app under test that does *not*
# share a database connection with the specs, so use truncation strategy.
DatabaseCleaner.strategy = :truncation
end
end
# adds this from
# https://stackoverflow.com/questions/37753251/actionmailer-not-delivering-confirmation-emails-in-test-environment-rails-4
config.before(:each, truncation: true) do
Database::Cleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.append_after(:each) do
DatabaseCleaner.clean
end
end
UPDATED
Output for rails routes
Prefix Verb URI Pattern Controller#Action
root GET / welcome#index
passwords POST /passwords(.:format) clearance/passwords#create
new_password GET /passwords/new(.:format) clearance/passwords#new
session POST /session(.:format) clearance/sessions#create
edit_user_password GET /users/:user_id/password/edit(.:format) clearance/passwords#edit
user_password PATCH /users/:user_id/password(.:format) clearance/passwords#update
PUT /users/:user_id/password(.:format) clearance/passwords#update
POST /users/:user_id/password(.:format) clearance/passwords#create
users POST /users(.:format) clearance/users#create
sign_in GET /sign_in(.:format) clearance/sessions#new
sign_out DELETE /sign_out(.:format) clearance/sessions#destroy
GET /sign_out(.:format) clearance/sessions#destroy
sign_up GET /sign_up(.:format) clearance/users#new
user_lives GET /user/lives(.:format) user/lives#index
POST /user/lives(.:format) user/lives#create
new_user_life GET /user/lives/new(.:format) user/lives#new
edit_user_life GET /user/lives/:id/edit(.:format) user/lives#edit
user_life GET /user/lives/:id(.:format) user/lives#show
PATCH /user/lives/:id(.:format) user/lives#update
PUT /user/lives/:id(.:format) user/lives#update
DELETE /user/lives/:id(.:format) user/lives#destroy
user_codes GET /user/codes(.:format) user/codes#index
POST /user/codes(.:format) user/codes#create
new_user_code GET /user/codes/new(.:format) user/codes#new
edit_user_code GET /user/codes/:id/edit(.:format) user/codes#edit
user_code GET /user/codes/:id(.:format) user/codes#show
PATCH /user/codes/:id(.:format) user/codes#update
PUT /user/codes/:id(.:format) user/codes#update
DELETE /user/codes/:id(.:format) user/codes#destroy
lives GET /lives(.:format) lives#index
life GET /lives/:id(.:format) lives#show
codes GET /codes(.:format) codes#index
code GET /codes/:id(.:format) codes#show

I believe this is an autoload issue. If your ::CodesController gets loaded before the Users::CodesController then your spec for Users::CodesController is actually using ::CodesController and hence can't create the correct routes. When run singly the correct class is autoloaded and everything works correctly. To fix this require the correct class at the beginning of your spec.
# spec/controllers/user/codes_controller_spec.rb
require 'user/codes_controller'
...
and
# spec/controllers/codes_controller_spec.rb
require 'codes_controller'
...

Related

In RSpec, running a patch or put test will result in an ActionController::UrlGenerationError: No route matches error

What I want to solve
I want the Rspec patch or put test to succeed.
I also tested PostsContoroller before this, and I am puzzled because I did not get the same error when testing PostsContoroller.
Error
Failures:
1) Api::V1::PostItemsController update Update Content
Failure/Error: patch :update, params: { post: post_params }
ActionController::UrlGenerationError:
No route matches {:action=>"update", :controller=>"api/v1/post_items", :post=>{:id=>1, :content=>"Update-Content", :status=>false, :post_id=>1}}
# ./spec/controllers/post_items_spec.rb:11:in `block (3 levels) in <main>'
Finished in 0.35529 seconds (files took 5.58 seconds to load)
5 examples, 1 failure
Code
FactoryBot
book.rb
FactoryBot.define do
factory :book, class: Post do
sequence(:id) { |n| n}
sequence(:title) { |n| "title#{n}" }
sequence(:author) { |n| "author#{n}" }
sequence(:image) { |n| "image#{n}"}
end
end
content.rb
FactoryBot.define do
factory :content, class: PostItem do
sequence(:id) { |n| n }
sequence(:content) { |n| "list#{n}"}
sequence(:status) { false }
end
end
Spec
post_items_spec.rb
require 'rails_helper'
RSpec.describe Api::V1::PostItemsController, type: :controller do
describe 'update' do
it 'Update Content' do
book = create(:book)
content = create(:content, post_id: book.id)
post_params = { id: content.id, content: 'Update-Content', status: false, post_id: book.id }
patch :update, params: { post: post_params }
json = JSON.parse(response.body)
expect(response.status).to eq(200)
expect(json['Update-Content']).to eq('Update-content')
end
end
end
Routes
**Rails.application.routes.draw do
namespace :api do
namespace :v1 do
resources :posts
resources :post_items
end
end
end
The use of controller specs is discouraged by both the Rails and RSpec teams and has been for a very long time now. You should be writing a request spec instead which sends real HTTP requests.
RSpec.describe 'Api V1 Post items', type: :request do
let(:book) { create(:book) }
describe "PATCH /api/v1/books" do
context "with valid parameters" do
subject do
patch api_v1_post_item_path(book),
params: { content: 'Update-Content' }
end
it { should be_successful }
it "updates the content" do
# refresh the record from the db
expect { book.reload }.to change(book, :title).to('Update-Content')
end
it "includes the updated entity in the response body" do
expect(response.parsed_body['content']).to eq 'Update-Content'
end
end
# #todo write specs with invalid parameters
# #todo write specs for authentication and authorization
end
end
Another problem is that you're generating IDs in your factory. Do not do this ever. When you're actually persisting records the database will automatically assign ids. When you use build_stubbed FactoryBot will create a mock id. Using a sequence to generate IDs invites bad practices such as hardcoding ids into a spec and will only cause you headaches.
If you really want to salvage that controller spec the routing error is caused by the fact that you're missing an the ID parameter - since you're calling it as patch :update, params: { post: post_params } the id parameter is buried in params[:post][:id]. So you want patch :update, params: { id: post.id, post: post_params } I don't recommend this though - get with the program and write future proof tests instead that won't let all the bugs slip though.

rails - Objects persisted by FactoryGirl are not available in Controller

I am writing tests for my controllers in admin namespace. Using RSpec (3.5.0), FactoryGirl (4.8.0), DatabaseCleaner (1.5.3) and Mongoid (6.0.3).
The problem is that these test act strangely. When testing GET index request, objects produced by FactoryGirl are successfully created and persisted. However, the controller does not seem to find them.
I have three different controllers. 2 out of 3 have this problem and the third one works like a charm. The code is the same (except for naming) the only difference is that the resource for the working controller is nested.
The one for accessories works:
describe "GET #index", get: true do
let (:accessory) { FactoryGirl.create(:accessory) }
before do
get :index, params: { category_id: accessory.category_id.to_s }, session: valid_session, format: :json
end
it "responses with OK status" do
expect(response).to have_http_status(:success)
end
it "responses with a non-empty Array" do
expect(json_body).to be_kind_of(Array)
expect(json_body.length).to eq(1)
end
it "responses with JSON containing accessory" do
expect(response.body).to be_json
expect(json_body.first.with_indifferent_access).to match({
id: accessory.to_param,
name: 'Test accessory',
description: 'This is an accessory',
car_model: 'xv',
model_year: '2013',
images: be_kind_of(Array),
category_id: accessory.category.to_param,
dealer_id: accessory.dealer.to_param,
url: be_kind_of(String)
})
end
end
And the one for categories does not:
describe "GET #index", get: true do
let (:category) { FactoryGirl.create(:category) }
before do
get :index, params: {}, session: valid_session, format: :json
end
it "responses with OK status" do
expect(response).to have_http_status(:success)
end
it "responses with a non-empty Array" do
expect(json_body).to be_kind_of(Array)
expect(json_body.length).to eq(1)
end
it "responses with JSON containing category" do
expect(response.body).to be_json
expect(json_body.first.with_indifferent_access).to match({
id: category.to_param,
name: 'Test category',
image: be_kind_of(String),
url: be_kind_of(String)
})
end
end
As you can see the logic is the same: issuing a request in the before hook and using let to set the object.
Another strange thing is that GET show test for categories with the same logic works perfectly.
In these questions (1, 2) they say it might be due to DatabaseCleaner strategy and one should use truncation instead of transaction strategy. Which I do since Mongoid allows only truncation. And I am also not using JavaScript-enabled tests and specifically told rspec to use_transactional_fixtures = false
RSpec config for FactoryGirl and DatabaseCleaner:
RSpec.configure do |config|
config.include FactoryGirl::Syntax::Methods
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each, :js => true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
I am able to make these tests pass by issuing request and creating an object in each example instead of using before and let. But I think it should work with them.
Controller index methods are default:
def index
#thing = Thing.all
end
Do you have any thoughts on this strange behaviour?
Please try let! instead of let.
Note that let is lazy-evaluated. Category data is generated when you call category.to_param. It does not exist in the before block.
See also https://relishapp.com/rspec/rspec-core/v/3-5/docs/helper-methods/let-and-let

RSpec Routing Spec POST Request Problems

I'm a Ruby on Rails developer and I was testing a fairly simple Rails application using RSpec. I was writing some Routing specs and then I faced this problem:
My routes are like this:
Rails.application.routes.draw do
root 'trip_plans#index'
resources :trip_plans
end
So I have a route like post /trip_plans for creating new plans and triggering the trip_plans#create action.
My Routing spec file spec/routing/trip_plans_spec.rb looks like this:
require 'rails_helper'
RSpec.describe 'trip_plans routes', type: :routing do
describe 'creating a new plan' do
it 'creates a new plan on post in /trip_plans' do
expect(post: '/trip_plans').to route_to(controller: 'trip_plans', action: 'create', title: 'New Plan', day: '3')
end
end
end
Now I need to somehow pass the params title: 'New Plan', day: '3' to my expect(post: '/trip_plans') so it seems like a real user is filling in the forms and hitting submit.
How do I pass params for POST requests to my RSpec Routing spec?
Thanks in advance!
Routing specs don't often add much value. In a routing spec you simply test that a certain route matches the correct controller. The controller is never actually called.
Instead what you can use are controller specs which are used to test how your application responds to user input:
# spec/controllers/trip_plans_controller_spec.rb
RSpec.describe TripPlansController, type: :controller do
let(:valid_params) do
{
title: 'New Plan',
day: '3'
}
end
let(:invalid_params) do
{
day: 'xxx'
}
end
describe 'POST #create' do
let(:action) { post :create, valid_params }
context 'with valid attributes' do
it 'creates a new post' do
expect { action }.to change(Post, :count).by(+1)
end
it 'has the correct attrributes' do
action
expect(assigns(:trip_plan).title).to eq 'New Plan'
expect(assigns(:trip_plan).day).to eq 3
end
end
context 'with invalid attributes' do
let(:action) { post :create, invalid_params }
it 'does not create a new post' do
expect { action }.to_not change(Post, :count)
end
it 'renders the new template' do
action
expect(response).to render_template :new
end
end
end
end
and feature specs which are end to end specs which test the actual user experience:
RSpec.feature 'Trip Plans' do
context 'as a User' do
scenario 'I should be able to create a trip plan' do
visit root_path
click_link 'Create a new trip plan'
fill_in 'Title', with: 'Go west'
fill_in 'Day', with: 5
click_button 'Create trip plan'
expect(page).to have_content 'Trip plan created.'
expect(page).to have_content 'Go west'
end
end
end
Controller specs are very useful for testing exactly how your controller responds to params and where you write actual expectations on the database state.
Feature specs are nice since they cover your views as well and well written specs also guarantee that your user paths are accessible. However they often do not catch errors which are not readily apparent from the front end and are slower, since you often need to render several pages to get to the actual meat of the test.
The stack trace or error message from feature specs is often less useful than lower level specs.
A good test suite is usually made of a combination of model specs, controller specs and feature specs which cover the most important paths through the application.

Why does Rails fail to run a single rake test?

I am trying to troubleshoot a problem that I am having with my test database. I am currently following alongside Agile Web Development 4 and going through the chapters. Somewhere along 3/4ths of the way I discovered that my test database was persisting data and am currently trying to find out where exactly this is taking place. I have an orders_controller_test.rb file that I am trying to run.
When I execute
rake test:controllers
rake test test/controllers/orders_controller_test.rb
rake test
the tests execute and the data is persisted. I want to go test by test one at a time and am executing this command to do so:
rake test test/controllers/orders_controller_test.rb name_of_test
as shown in the Rails 4 guide. The execution of this command yields this:
As you can see it executes the tests but doesn't actually do so. No assertions are made and this is confusing/frustrating me. I've referred to this web article and have tried the methods in it. Different ways to run rails tests
Why is this not executing correctly. This is immensely frustrating and I suspect it is something simple. Can someone please help pinpoint what is possibly going on? Help would be greatly appreciated.
-----EDIT-----------
Controller tests
require 'test_helper'
class OrdersControllerTest < ActionController::TestCase
def setup
#order = orders(:one)
end
def teardown
#order = nil
end
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:orders)
end
test "should get new" do
get :new
assert_response :redirect
end
test "should create order" do
assert_difference('Order.count') do
post :create, order: { address: #order.address, email: #order.email, name: #order.name, pay_type: #order.pay_type }
end
assert_redirected_to store_path
end
test "should show order" do
get :show, id: #order
assert_response :success
end
test "should get edit" do
get :edit, id: #order
assert_response :success
end
test "should update order" do
patch :update, id: #order, order: { address: #order.address, email: #order.email, name: #order.name, pay_type: #order.pay_type }
assert_redirected_to order_path(assigns(:order))
end
test "should destroy order" do
assert_difference('Order.count', -1) do
delete :destroy, id: #order
end
assert_redirected_to orders_path
end
test "requires item in cart" do
get :new
assert_redirected_to store_path
assert_equal flash[:notice], 'Your cart is empty'
end
test "should get new order" do
item = LineItem.new
item.build_cart
item.product = products(:ruby)
item.save!
binding.pry
session[:cart_id] = item.cart.id
get :new
binding.pry
assert_response :success
end
end
It appears that your test method name is incorrect. I believe you have to prepend test_ to the method name.
rake test test/controllers/orders_controller_test.rb -n test_should_get_new_order
# or
rake test test/controllers/orders_controller_test.rb - test_should_get_new_order
Rails converts test 'some name' do to test_some_name to run the test(s).

Controller RSpec: test PATCH update using custom ID

I’m having a challenge write a RSpec controller test for a PATCH update, because the routing and edit uses a secure edit_id that my model generates, instead of the standard 1,2,3,4,5 (sequenced id) that Rails auto-generates. Basically, I’m not sure how to get my tests to lookup the request to be edited using this edit_id.
My test currently:
describe "PATCH edit/update" do
before :each do
#testrequest = FactoryGirl.build(:request, name: "James Dong")
end
it "located the requested #testrequest" do
patch :update, id: #testrequest.edit_id, request: FactoryGirl.attributes_for(:request)
assigns(:request).should eq(#testrequest)
end
describe "using valid data" do
it "updates the request" do
patch :update, #testrequest.name = "Larry Johnson"
#testrequest.reload
#testrequest.name.should eq("Larry Johnson")
end
end
FactoryGirl helper (I've tried both explicitly adding edit_id and not [i.e., relying on the model to create the edit_id itself], neither makes a difference) code:
FactoryGirl.define do
factory :request do |f|
f.name { Faker::Name.name }
f.email { Faker::Internet.email }
f.item { "random item" }
f.detail { "random text" }
f.edit_id { "random" }
end
end
Controller:
def update
#request = Request.find_by_edit_id(params[:edit_id])
if #request.update_attributes(request_params)
flash[:success] = "Your request has been updated! We'll respond within one business day."
redirect_to edit_request_path(#request.edit_id)
else
render 'edit'
end
end
Routing:
get 'edit/:edit_id', to: 'requests#edit', as: 'edit_request'
patch 'requests/:edit_id', to: 'requests#update', as: 'request'
Ok someone helped me figure this out, and I feel very silly. The "id" that you pass to the patch method can be any id, so instead of trying to set id: edit_it, I should use edit_it in the first place. I.e., the code that works:
before :each do
#testrequest = FactoryGirl.build(:request, name: "James Dong")
end
it "located the requested #testrequest" do
patch :update, edit_id: #testrequest.edit_id, request: FactoryGirl.attributes_for(:request)
assigns(:request).should eq(#testrequest)
end
describe "using valid data" do
it "updates the request" do
patch :update, edit_id: #testrequest.edit_id, request: FactoryGirl.attributes_for(:request, name: "Larry Johnson")
#testrequest.reload
#testrequest.name.should eq("Larry Johnson")
end
end

Resources