ControllerTest error, Couldn't find Transaction with 'id'= - ruby-on-rails

The Show action for my Controller is not passing the unit test. I'm getting an error in my ControllerTest. Here is the error I get from 'rake test':
> ERROR["test_should_get_show", TransactionsControllerTest, 2015-07-18
> 00:30:18 -0400] test_should_get_show#TransactionsControllerTest
> (1437193818.29s) ActiveRecord::RecordNotFound:
> ActiveRecord::RecordNotFound: Couldn't find Transaction with 'id'=
> app/controllers/transactions_controller.rb:21:in `show'
> test/controllers/transactions_controller_test.rb:6:in `block in <class:TransactionsControllerTest>'
> app/controllers/transactions_controller.rb:21:in `show'
> test/controllers/transactions_controller_test.rb:6:in `block in <class:TransactionsControllerTest>'
>
> 5/5:
> [=======================================================================================================================] 100% Time: 00:00:00, Time: 00:00:00
>
> Finished in 0.24993s 5 tests, 4 assertions, 0 failures, 1 errors, 0
> skips
Despite this error, the view is successfully generates the page in the browser when I use this url:
http://localhost:3000/transactions/1
So, there is an error somewhere. It's not failing.
transactions_controller.rb
class TransactionsController < ApplicationController
def new
#transaction = Transaction.new
end
def create
#transaction = Transaction.new(transaction_params)
if #transaction.save
# Handle a successful save.
else
render 'new'
end
end
def index
#transactions = Transaction.all
end
def show
#transaction = Transaction.find(params[:id]) #this is row 21
end
private
def transaction_params
params.require(:transaction).permit(:company, :month, :account_code, :description, :transaction_amount)
end
end
transaction_controller_test.rb
require 'test_helper'
class TransactionsControllerTest < ActionController::TestCase
test "should get show" do
transaction = Transaction.create
get :show, id: transaction.id #this is row 6
assert_response :success
end
end
The following fixture was automatically generated by rails. I'm new at rails and have to admit that I don't really understand fixtures.
transactions.yml
one:
company: MyString
month: 2015-06-19
account_code: MyString
description: MyString
transaction_amount: 1.5
two:
company: MyString
month: 2015-06-19
account_code: MyString
description: MyString
transaction_amount: 1.5
In my routes file, I'm setting the root to the index action. I plan to access the show action via the url http://localhost:3000/transactions/1 and (as mentioned above) it actually works.
routes.rb
Rails.application.routes.draw do
root 'transactions#index'
resources :transactions
end
transaction.rb
class Transaction < ActiveRecord::Base
validates :month, presence: true
validates :account_code, presence: true
end
transaction_test.rb
require 'test_helper'
class TransactionTest < ActiveSupport::TestCase
def setup
##transaction = transactions(:one)
#transaction = Transaction.new(company: "Inc", month: Date.new(2015,6,15).to_s,
account_code: "80-32100-12-1201-60010",
description: "Salaries & Wages - Histo Gross 1", transaction_amount: 100000)
end
test "should be valid" do
assert #transaction.valid?
end
test "month should be present" do
#transaction.month = " "
assert_not #transaction.valid?
end
test "account_code should be present" do
#transaction.account_code = " "
assert_not #transaction.valid?
end
test "month cannot be a string" do #not sure if this is worth anything at all
#transaction.month = "xxxxxxxxxxxx"
assert_not #transaction.valid?
end
end
The test auto generated by Rails (before I modified it) was very simple:
require 'test_helper'
class TransactionsControllerTest < ActionController::TestCase
test "should get show" do
get :show
assert_response :success
end
end
I modified it to the current version above because (I believe) the test should first create a transaction and then reference id somehow. But I'm not sure I did this correctly.
I've seen questions similar to this on Stackoverflow. For instance, this question was helpful, but didn't quite get me there
Count; find User with id= Minitest
NOTE: Initially, I set up the Controller incorrectly. I set it up as a singular "TransactionController" and changed it to the plural "TransactionsController". So I had to change the name in the unit test also. Perhaps this has had an effect on the app.
So... questions:
why is the unit test failing while the show action is actually
working in the browser?
How might I change the code in the unit test so it recognizes that
I'm using transaction_params?
Do I have to modify the fixtures when I make changes to the
unit_test or the controller?

Ok, so coorasse's suggestion to use create! forced the app to uncover a validation error. The initial error (Couldn't find Transaction with 'id'= ) was replaced with a new error:
ActiveRecord::RecordInvalid: Validation failed: Month can't be blank,
Account code can't be blank
This was confusing because it leads me to think of a model validation error. Why am I getting a model validation error in a ControllerTest? Also, the transaction.rb model clearly shows that that I've added validations to satisfy the presence of Month and AccountCode.
So, I spent some time reading through "A Guide to Testing Rails Applications" on RailsGuides
http://guides.rubyonrails.org/testing.html
All the way down in Section 8, it shows how to include a block of code before each test. Here is the new transaction_controller_test
transaction_controller_test.rb (REVISED)
require 'test_helper'
class TransactionsControllerTest < ActionController::TestCase
# called before every single test
def setup
#transaction = transactions(:one)
end
test "should get show" do
get :show, id: #transaction.id
assert_response :success
end
end
After this the test passed! Unfortunately, the RailsGuides doesn't tell me why I would want to use setup in a controller test. If any smart Rails gurus has some guidance on this, your comments would be appreciated.

Related

Rails 6 Testing - Initializers and dynamic data with Minitest

I'm currently writing tests for my Rails application using the default Minitest framework and it's working quite fine. The only problem I have that my configuration initializer is run (I guess) every test twice and I can't figure out why this happens.
So, I have an initializer which sets some config parameters from the database for my application:
# config/initializers/load_application_settings.rb
Rails.configuration.after_initialize do
if defined?(::Rails::Server) && !Rails.env.test?
WebMenueNew::Application.config.header_title = "WebMenue | #{Configuration.first.clinicName}"
WebMenueNew::Application.config.clinic_name = Configuration.first.clinicName
WebMenueNew::Application.config.clinic_location = Configuration.first.city
WebMenueNew::Application.config.proxy_server = Configuration.first.request_server
WebMenueNew::Application.config.order_time = Configuration.first.orderTime.strftime('%H:%M')
WebMenueNew::Application.config.repeating_period = Configuration.first.repeatMealTime
WebMenueNew::Application.config.clinic_iban = Configuration.first.iban
WebMenueNew::Application.config.clinic_bic = Configuration.first.bic
WebMenueNew::Application.config.clinic_bank = Configuration.first.bank
end
end
I tried to wrap it witht he if !Rails.env.test? condition, but it doesn't seem to work. In my controller test, I tried to initialize the value in the setup method and destroy all params in the teardown method:
require 'test_helper'
class SessionsControllerTest < ActionDispatch::IntegrationTest
setup do
Rails.configuration.clinic_name = "BDH-Klinik Braunfels"
#user = users(:one)
end
test "should get new" do
get root_path
assert_response :success
assert_select "title", "WebMenue | BDH-Klinik Braunfels"
end
test "should be valid external login" do
post login_path, params: { session: { using_ldap: false, username: #user.email, password: "valid_password" } }
assert_response :redirect
assert_redirected_to #user
end
teardown do
Configuration.destroy_all
end
end
Even if i move it out of the setup method and into the test method for the new action, it always produces two record in the test database and I get the following error:
Minitest::UnexpectedError: ActiveRecord::RecordNotUnique:
PG::UniqueViolation: ERROR: duplicate key value violates unique
constraint "configurations_pkey"
DETAIL: Key (id)=(980190962) already exists.
In my new view for the sessions controller I set the title of the application dynamically, that's why I need the parameter:
<% content_for(:title) { "WebMenue | #{Rails.configuration.clinic_name}" } %>
Is there any workaround so that the initializer is not run on test suites? Or am I missing something? I'm relatively new to testing in Rails so don't be too hard :).

How to do Integration Test create through other model

1) I have this model Job and the model institution
class Job < ApplicationRecord
belongs_to :institution
# others attibutes
end
2) This is my action create on JobsController - I need a institution to create a job. it is fine.
def create
build_job
save_job || render(:new, status: :unprocessable_entity)
end
3) This is the integration test that I created
I am not getting the success test
In params
-I also tried institution: #institution
-and also tried institution_id: #institution.id
require 'test_helper'
class JobActionsTest < ActionDispatch::IntegrationTest
setup do
#user = users(:standard)
sign_in #user
#institution = institutions(:standard)
end
test "can create a job through institution" do
get new_institution_job_path(#institution)
assert_response :success
assert_difference('Job.count') do
post jobs_path,
params: {job: {title: "Desenvolvedor", description: "Ruby",
requirements: "rspec and capybara",
start_date: Date.today,
end_date: Date.today + 5.days,
institution: #institution.id}}
end
assert_response :redirect
follow_redirect!
assert_response :success
end
end
4) And this is my console error
#Running:
E
Error:
JobActionsTest#test_can_create_a_job_through_institution:
ActiveRecord::RecordNotFound: Couldn't find Institution with 'id'=
app/controllers/jobs_controller.rb:74:in `job_scope'
app/controllers/jobs_controller.rb:52:in `build_job'
app/controllers/jobs_controller.rb:18:in `create'
test/integration/job_actions_test.rb:22:in `block (2 levels) in <class:JobActionsTest>'
test/integration/job_actions_test.rb:21:in `block in <class:JobActionsTest>'
bin/rails test test/integration/job_actions_test.rb:17
Start by nesting the jobs resource properly:
resources :institutions do
resources :jobs, only: [:new, :create]
end
# or to create the full suite
resources :institutions do
resources :jobs, shallow: true
end
This will give these routes:
Prefix Verb URI Pattern Controller#Action
institution_jobs POST /institutions/:institution_id/jobs(.:format) jobs#create
new_institution_job GET /institutions/:institution_id/jobs/new(.:format) jobs#new
...
Note that :institution_id is a now a part of URI pattern for the create route, and it will be available as params[:institution_id].
In your test you want to POST to /institutions/:institution_id/jobs:
require 'test_helper'
class JobActionsTest < ActionDispatch::IntegrationTest
setup do
#user = users(:standard)
sign_in #user
#institution = institutions(:standard)
end
# Use separate examples per route / case
test "can fill in form to create a new job" do
get new_institution_job_path(#institution)
assert_response :success
end
test "can create a job through institution" do
assert_difference ->{ #institution.jobs.count } do
post institution_jobs_path(#institution),
params: {
job: {
title: "Desenvolvedor",
description: "Ruby",
requirements: "rspec and capybara",
start_date: Date.today,
end_date: Date.today + 5.days
}
}
end
assert_redirected_to #institution.jobs.last
follow_redirect!
assert_response :success
end
end
Further you want to test that the job actually was created for the right institution. We do that by passing the lambda ->{ #institution.jobs.count }.
And that the users are redirected to the correct resource - not just somewhere - which is done with assert_redirected_to #institution.jobs.last.
It looks like that when you call at line 22
get new_institution_job_path(#institution)
the #institution object you have built in the setup block is not saved in the database.
The error you are receiving, ActiveRecord::RecordNotFound, says that it cannot be found an Institution with id nil.
You can easily check if I am guessing correctly by adding this assertion:
test "can create a job through institution" do
assert_not_nil(#institution.id) # or assert_not_equal(0, Institution.where(id: #institution.id).size)
get new_institution_job_path(#institution)
assert_response :success
#...
end
Make sure that your institutions(:standard) method looks like Institution.create!() and not like Institution.new or Institution.build

Trying to retrieve a record with get :index, based on user, nothing getting returned. Response OK

I'm trying to retrieve a record based on a user id. To make sure that my get controller's index works appropriately. Here is my controller snippet.
class SimulationsController < ApplicationController
def index
if current_user
#simulations = current_user.simulations
else
redirect_to new_user_session_path, notice: 'You are not logged in.'
end
end
Now I put some traces in my below controller spec. From what I'm gatherin from these traces there are four Simulations that exist for the test with the User_ID 1, however the User thats being created for the test, and the records along with it in the test are all User_ID 5. Can anyone give some guidance I'm pretty stumped beating my head on this. Additionally I'm getting a Response: OK. EDIT: Updated Spec with below answer.
require 'spec_helper'
describe SimulationsController, :type => :controller do
let :user do
FactoryGirl.create(:user)
end
before(:each) do
puts "~#{user.id}"
sign_in user
end
describe "GET #index" do
it "returns the correct number of simulations" do
simulation = FactoryGirl.build(:simulation, user: user)
simulation.save!
puts "#####{simulation.user_id}"
puts user.id
Simulation.all.each do |sim|
puts sim.user_id
end
get :index
puts "---\t\t#{response.body.size}"
# expect(response).to be_success
end
end
end
EDIT 2:
User Factory:
FactoryGirl.define do
factory :user do
email "user_#{User.last.nil? ? 1 : User.last.id + 1}#home.com"
password "password"
end
end
Simulation Factory:
FactoryGirl.define do
factory :simulation do |f|
f.id (Simulation.last.nil? ? 1 : Simulation.last.id + 1)
f.x_size 3
f.y_size 3
f.user_id 1
end
end
Final Edit: I was going about checking wrong, as outlined below, Body isn't what I was looking for I wanted to use assigns as I did below to check what I wanted too:
it "returns the correct number of simulations" do
simulation = FactoryGirl.build(:simulation, user: user)
simulation.save!
get :index
expect(assigns(:simulations).size).to eq 1
end
response.body is the rendered HTML, so its size would be length of that string. Depending on what your view looks like there might not be a straightforward correlation between its size and the number of simulations rendered.
In addition by default rspec doesn't render views at all in controller specs and so response.body will always be an empty string. You can change this by adding render_views to the example group.
Probably FactoryGirl overrides your user_id assignment, because you have association :user being set up there. Just change user_id to user and it should work:
simulation = FactoryGirl.build(:simulation, user: user)
UPD. And fix your factory:
FactoryGirl.define do
factory :simulation do |f|
# Never set ID manually
# f.id (Simulation.last.nil? ? 1 : Simulation.last.id + 1)
f.x_size 3
f.y_size 3
# f.user_id 1
# user 'association' method to set up associations
f.association :user
end
end
UPD2. To check if you controller assigned variable properly, use assigns:
expect(assigns(:simulations).length).to eq 4
You almost never should compare your response.body to anything – because, well, it's just raw body. To test your views you use special expectation methods, and to check instance #-variables assignments you use assigns.

Create a post controller spec test

I am doing a controller test but it seems that spec.rb is wrong.
Do you have a suggestion ?
This is my posts_controller.rb:
class PostsController < ApplicationController
def create
#post = Post.new(post_params)
if #post.save
redirect_to #wall
end
end
def destroy
#post.destroy
end
private
def post_params
params.require(:post).permit(:wall, :content)
end
end
and this is my posts_controller_spec.rb:
require 'rails_helper'
describe PostsController do
let(:wall) { create(:wall) }
describe "#create" do
it "saves the new post in the wall" do
post :create, { wall_id: wall, content: "Some text I would like to put in my post" }
end
end
describe "#destroy" do
it "deletes the post in the wall" do
end
end
end
could you please help me to correct my spec.rb?
this is my error:
PostsController
#create
saves the new post in the wall (FAILED - 1)
#destroy
deletes the post in the wall
Failures:
1) PostsController#create saves the new post in the wall
Failure/Error: post :create, post: { wall: wall, content: "Some text I would like to put in my post" }
ActiveRecord::AssociationTypeMismatch:
Wall(#2159949860) expected, got String(#2155957040)
# ./app/controllers/posts_controller.rb:3:in create'
# ./spec/controllers/posts_controller_spec.rb:8:inblock (3 levels) in '
# -e:1:in `'
Finished in 0.9743 seconds (files took 3.94 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./spec/controllers/posts_controller_spec.rb:7 # PostsController#create saves the new post in the wall
Thank you in advance
Your spec doesn't include any expectations, so it's "wrong" in that sense. I suggest you google "RSpec expectations" and/or read the docs (i.e. https://relishapp.com/rspec/rspec-expectations/docs).
As for the error you mentioned in your comment, that reflects a problem with your production code (i.e. the lack of a redirect or render or some create template in the case the #post.save returns nil). Again, googling the error should yield information to help you address this problem or your can read http://guides.rubyonrails.org/layouts_and_rendering.html. If you're new to Rails entirely, I suggest following one of the tutorials, such as https://www.railstutorial.org/
You should also update your question to include that error information, since it's highly relevant and the question is essentially incomplete without it.
You should expect something on your tests. For example, you could do like this:
RSpec.describe PostsController, type: :controller do
let!(:wall) { create(:wall) }
let(:test_post) {
create(:post, wall_id: wall.id, content: "Something") }
}
describe "POST #create" do
let(:post) { assigns(:post) }
let(:test_wall) { create(:wall) }
context "when valid" do
before(:each) do
post :create, params: {
post: attributes_for(:post, wall_id: test_wall.id, content: "Anything")
}
end
it "should save the post" do
expect(post).to be_persisted
end
end
end
end
This way you are expecting a response from rails when you post you parameters. I just coded the post part of the test.

get method for testing in rails

I'm following along with RailsSpace: Building a Social Networking Website with Ruby on Rails by Michael Hartl. Running rails v2.3.2.
I've gotten to the 5th chapter in which testing is introduced. The following is supposed to match the title of the various pages to strings using the get method:
require File.dirname(__FILE__) + '/../test_helper'
require 'site_controller'
# Re-raise errors caught by the controller.
class SiteController; def rescue_action(e) raise e end; end
class SiteControllerTest < Test::Unit::TestCase
def setup
#controller = SiteController.new
#request = ActionController::TestRequest.new
#response = ActionController::TestResponse.new
end
def test_index
get :index
title = assigns(:title)
assert_equal "Welcome to RailsSpace!", title
assert_response :success
assert_template "index"
end
def test_about
get :title
title = assigns(:title)
assert_equal "About RailsSpace", title
assert_response :success
assert_template "about"
end
def test_help
get :help
title = assigns(:title)
assert_equal "RailsSpace Help", title
assert_response :success
assert_template "help"
end
end
On compiling I get:
Loaded suite site_controller_test
Started
EEE
Finished in 0.057 seconds.
1) Error:
test_about(SiteControllerTest):
NoMethodError: undefined method `get' for #<SiteControllerTest:0x4854b30>
site_controller_test.rb:23:in `test_about'
2) Error:
test_help(SiteControllerTest):
NoMethodError: undefined method `get' for #<SiteControllerTest:0x4854b1c>
site_controller_test.rb:31:in `test_help'
3) Error:
test_index(SiteControllerTest):
NoMethodError: undefined method `get' for #<SiteControllerTest:0x485470c>
site_controller_test.rb:15:in `test_index'
3 tests, 0 assertions, 0 failures, 3 errors
Other people have had this issue and the only proposed solution is just to reinstall. I'm not to enthused by this. Since this is an older book there this is probably just breakage between rails versions. What would be the equivalent of this for rails v2.3.2?
Replace all the following code
# Re-raise errors caught by the controller.
class SiteController; def rescue_action(e) raise e end; end
class SiteControllerTest < Test::Unit::TestCase
def setup
#controller = SiteController.new
#request = ActionController::TestRequest.new
#response = ActionController::TestResponse.new
end
with
class SiteControllerTest < ActionController::TestCase
The code you are using refers to Rails 2.0/2.1.
Try changing Test::Unit::TestCase to ActionController::TestCase.
One other thing you might like to know is that Railspace has evolved into Insoshi so the latest code is available there. Might be handy for you when you run into other issues.

Resources