How to do Integration Test create through other model - ruby-on-rails

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

Related

RSpec: Controller spec with polymorphic resource, "No route matches" error

I'm learning RSpec by writing specs for an existing project. I'm having trouble with a controller spec for a polymorphic resource Notes. Virtually any other model can have a relationship with Notes like this: has_many :notes, as: :noteable
In addition, the app is multi-tenant, where each Account can have many Users. Each Account is accessed by :slug instead of :id in the URL. So my mulit-tenant, polymorphic routing looks like this:
# config/routes.rb
...
scope ':slug', module: 'accounts' do
...
resources :customers do
resources :notes
end
resources :products do
resources :notes
end
end
This results in routes like this for the :new action
new_customer_note GET /:slug/customers/:customer_id/notes/new(.:format) accounts/notes#new
new_product_note GET /:slug/products/:product_id/notes/new(.:format) accounts/notes#new
Now on to the testing problem. First, here's an example of how I test other non-polymorphic controllers, like invitations_controller:
# from spec/controllers/accounts/invitation_controller_spec.rb
require 'rails_helper'
describe Accounts::InvitationsController do
describe 'creating and sending invitation' do
before :each do
#owner = create(:user)
sign_in #owner
#account = create(:account, owner: #owner)
end
describe 'GET #new' do
it "assigns a new Invitation to #invitation" do
get :new, slug: #account.slug
expect(assigns(:invitation)).to be_a_new(Invitation)
end
end
...
end
When i try to use a similar approach to test the polymorphic notes_controller, I get confused :-)
# from spec/controllers/accounts/notes_controller_spec.rb
require 'rails_helper'
describe Accounts::NotesController do
before :each do
#owner = create(:user)
sign_in #owner
#account = create(:account, owner: #owner)
#noteable = create(:customer, account: #account)
end
describe 'GET #new' do
it 'assigns a new note to #note for the noteable object' do
get :new, slug: #account.slug, noteable: #noteable # no idea how to fix this :-)
expect(:note).to be_a_new(:note)
end
end
end
Here I'm just creating a Customer as #noteable in the before block, but it could just as well have been a Product. When I run rspec, I get this error:
No route matches {:action=>"new", :controller=>"accounts/notes", :noteable=>"1", :slug=>"nicolaswisozk"}
I see what the problem is, but i just can't figure out how to address the dynamic parts of the URL, like /products/ or /customers/.
Any help is appreciated :-)
UPDATE:
Changed the get :new line as requested below to
get :new, slug: #account.slug, customer_id: #noteable
and this causes the error
Failure/Error: expect(:note).to be_a_new(:note)
TypeError:
class or module required
# ./spec/controllers/accounts/notes_controller_spec.rb:16:in `block (3 levels) in <top (required)>'
Line 16 in the spec is:
expect(:note).to be_a_new(:note)
Could this be because the :new action in my notes_controller.rb is not just a #note = Note.new, but is initializing a new Note on a #noteable, like this?:
def new
#noteable = find_noteable
#note = #noteable.notes.new
end
Well the problem here should be that in this line
get :new, slug: #account.slug, noteable: #noteable
you are passing :noteable in params. But, you need to pass all the dynamic parts of the url instead to help rails match the routes. Here you need to pass :customer_id in params. Like this,
get :new, slug: #account.slug, customer_id: #noteable.id
Please let me know if this helps.

Rails 4: Factory Girl & Rspec with associated Model

I previously fixed an issue with some code that works though it is a little ugly. Problem now is that it breaks my tests! The idea here is that I can create a Campaign and associate 1 zip-file and one-to-many pdfs.
Previous question and solution:
Rails 4.2: Unknown Attribute or Server Error in Log
Here is the failure message:
console
1) CampaignsController POST #create with valid params
Failure/Error: post :create, campaign: attributes_for(:campaign)
ActiveRecord::RecordNotFound:
Couldn't find Uploadzip with 'id'=
# ./app/controllers/campaigns_controller.rb:15:in `create'
# ./spec/controllers/campaigns_controller_spec.rb:36:in `block (4 levels) in <top (required)>'
..and the rest of the code.
spec/factories/campaigns.rb
FactoryGirl.define do
factory :campaign do |x|
x.sequence(:name) { |y| "Rockfest 201#{y} Orange County" }
x.sequence(:comment) { |y| "Total attendance is #{y}" }
end
end
spec/controllers/campaigns_controller.rb
describe "POST #create" do
context "with valid params" do
before(:each) do
post :create, campaign: attributes_for(:campaign)
end
.........
end
app/controllers/campaigns_controller.rb
class CampaignsController < ApplicationController
......................
def create
#campaign = Campaign.new(campaign_params)
if #campaign.save
zip = Uploadzip.find(params[:uploadzip_id])
zip.campaign = #campaign
zip.save
flash[:success] = "Campaign Successfully Launched!"
redirect_to #campaign
else
................
end
end
.......................
private
def campaign_params
params.require(:campaign).permit(:name, :comment, :campaign_id, uploadpdf_ids: [])
end
end
This appears simple and I assume it is, yet I've tried quit a few things and can't seem to get it to pass. How would I support the new controller logic in this test? Any help is appreciated.
UPDATE
With zetitic's advice, I created the following code in which successfully passes.
before(:each) do
#uploadzip = create(:uploadzip)
post :create, campaign: attributes_for(:campaign), uploadzip_id: #uploadzip
end
Add the uploadedzip_id to the posted params:
before(:each) do
post :create, campaign: attributes_for(:campaign), uploadedzip_id: 123456
end

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).

Count; find User with id= Minitest

I am following Michael Hartl's Ruby on Rails tutorial and I am not sure why I am getting this Error when according to the tutorial everything should pass:
1) Error:
UsersControllerTest#test_should_get_show:
ActiveRecord::RecordNotFound: Couldn't find User with 'id'=
app/controllers/users_controller.rb:7:in `show'
test/controllers/users_controller_test.rb:10:in `block in <class:UsersControllerTest>'
My minitest:
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
# add invalid information and test that the User.count never changes
# also test that the sign up path is visited after invalid sign up
test "invalid signup information" do
# visit the signup path using get
get signup_path
assert_no_difference "User.count" do
post users_path, user: { name: "", email: "user#invalid", password: "foo", password_confirmation: "bar"}
end
assert_template "users/new"
end
end
I compared my users_controller to the official github tutorial and it looks the same
Users controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def show
#user = User.find(params[:id])
end
def create
# strong parameters
#user = User.new(user_params)
if #user.save
# handle save
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
end
I dont really understand why id is being searched for as well. My database is empty with no users. I am currently testing that inputing invalid parameters for sign up will not add another user.
my UserControllerTest:
require 'test_helper'
class UsersControllerTest < ActionController::TestCase
test "should get new" do
get :new
assert_response :success
end
test "should get show" do
get :show
assert_response :success
end
end
Show renders a page for specific user, so you need to pass it the id param. Change the test to:
test "should get show" do
user = User.create
get :show, id: user.id
assert_response :success
end
FYI, A small breakdown of the error message:
1) Error:
Error
UsersControllerTest#test_should_get_show:
In test test_should_get_show in class UserControllerTest
ActiveRecord::RecordNotFound: Couldn't find User with 'id'=
Database doesn't contain User object with empty id
app/controllers/users_controller.rb:7:in `show'
File and line that directly caused the error
test/controllers/users_controller_test.rb:10:in `block in <class:UsersControllerTest>'
File and line where the action originated from.

Test controller method. Rspec

I want to test the controller method, but I can not find the example of testing method with order and search .
This is my controller:
class Admin::HotelsController < Admin::BaseController
helper_method :sort_column, :sort_direction
def index
#hotels = Hotel.search(params[:search], params[:search_column]).order(sort_column + ' ' + sort_direction)
end
def show
#hotel = Hotel.find(params[:id])
end
def update
#hotel = Hotel.find(params[:id])
if #hotel.update_attributes(hotel_params)
redirect_to admin_hotels_path
else
render(:edit)
end
end
private
def hotel_params
params.require(:hotel).permit(:title, :description, :user_id, :avatar, :price, :breakfast, :status, address_attributes: [:state, :country, :city, :street])
end
def sort_column
Hotel.column_names.include?(params[:sort]) ? params[:sort] : 'created_at'
end
def sort_direction
%w[asc desc].include?(params[:direction]) ? params[:direction] : 'asc'
end
end
This is test for this controller.
require 'rails_helper'
describe Admin::HotelsController do
login_admin
describe 'GET index' do
it 'render a list of hotels' do
hotel1, hotel2 = create(:hotel), create(:hotel)
get :index
expect(assigns(:hotels)).to match_array([hotel1, hotel2])
end
end
describe 'GET show' do
it 'should show hotel' do
#hotel = create(:hotel)
get :show, { id: #hotel.to_param, template: 'hotels/show' }
expect(response).to render_template :show
end
end
end
I don't know how testing index method. Please help or give me a link with information about this. Thanks!
If it may help you, I personally prefer to have minimals tests for the controllers for various reasons:
1) as I was beginning in rails testing I read many articles saying it's a good idea
2) it allows you to tests in isolation model methods:
describe 'GET index' do
it 'render a list of hotels' do
hotel1, hotel2 = create(:hotel), create(:hotel)
get :index
expect(assigns(:hotels)).to match_array([hotel1, hotel2])
end
end
here your test matches the result of your query on the model. You can split it like this:
describe 'GET index' do
it 'render a list of hotels' do
hotel1, hotel2 = create(:hotel), create(:hotel)
Hotel.should_receive(:search).with(YOUR PARAMS)
get :index
response.response_code.should == 200
end
end
and then test the result of Hotel.search in a model test.
3) it allows you to test the feature and not some random things that are not really relevant:
describe 'GET show' do
it 'should show hotel' do
#hotel = create(:hotel)
get :show, { id: #hotel.to_param, template: 'hotels/show' }
expect(response).to render_template :show
end
end
here "expect(response).to render_template :show" seems like testing that rails rendering system is properly working. I assume that's not what you want to test, you may prefer (that's what I would do):
describe 'GET show' do
it 'should show hotel' do
#hotel = create(:hotel)
Hotel.should_receive(:find).with(YOUR PARAMS)
get :show, { id: #hotel.to_param, template: 'hotels/show' }
response.response_code.should == 200
end
end
and then test what is supposed to appear on the web page with a feature test using something like capybara gem unless you're rendering some json: in this case match the json values in the controller.
By the way: "#hotel = create(:hotel)" the # is not necessary here as you're in the "it". Moreover you can create such entry like this:
context "" do
before(:each) do
#hotel = create(:hotel) # here the # is necessary for the variable to be
end # accessible in the it
it "" do
end
end
or even like this:
context "" do
let(:hotel) { create(:hotel) } # you can call it in the test by using hotel and it
it "" do # will be insert in you db only when it's in the "it"
end # if you want it to be created in the "it" without
end # calling hotel for nothing, use let!
I would suggest using
describe 'GET index' do
let(:hotel1) { create(:hotel) }
let(:hotel2) { create(:hotel) }
it 'render index template' do
get :index
expect(response).to render_template :index
end
it 'render asc ordered hotels' do
get :index
# if you are using json responses
json = JSON.parse(response.body)
expect(json['hotels'].first).to eq hotel1
expect(json['hotels'].last ).to eq hotel2
# or any similar approach to get test the hotels in response
end
it 'render desc ordered hotels' do
get :index, {direction: 'desc'}
# if you are using json responses
json = JSON.parse(response.body)
expect(json['hotels'].first).to eq hotel2
expect(json['hotels'].last ).to eq hotel1
# or any similar approach to get test the hotels in response
end
# you can complete these tests yourself
it 'render hotels sorted with different_column_than_created_at asc'
it 'render hotels sorted with different_column_than_created_at desc'
end

Resources