NameError: uninitialized constant on RSpec - ruby-on-rails

I have the following in my UserTrainingController
def create
#user_training_resource = UserTrainingResource::Create.call(user_training_resource_params)
respond_with(#user_training_resource)
end
Then in Operations > Training Resource > Create
class UserTrainingResource
class Create < Operation
def call(params)
user_training_resource = UserTrainingResource.new(params)
ResourceMailer.requested(user_training_resource).deliver_later if user_training_resource.save
user_training_resource
end
end
end
Then in my test I have
require 'rails_helper'
RSpec.describe UserTrainingResource::Create do
let(:params) { attributes_for(:user_training_resource) }
describe '#call' do
it 'saves the request as pending' do
ut = UserTrainingResource::Create.call(params)
expect(ut.persisted?).to eq(true)
end
it 'queues a mailer' do
expect(ResourceMailer).to send_mail(:requested)
UserTrainingResource::Create.call(params)
end
end
end
The test gives me
NameError: uninitialized constant UserTrainingResource::Create
I've no idea what the issue is. Is it not hitting my operations correctly?

Rails expects that class to be defined in app/models/user_training_resource/create.rb, or you could add the dir to config.autoload_paths.
https://blog.bigbinary.com/2015/11/05/how-constant-lookup-happens-in-rails.html

You should use module, like this:
module UserTrainingResource
class Create < Operation
...
end
end
Or the shorthand way, like this:
class UserTrainingResource::Create < Operation
end

The file structure must match the name of the class. Rename the directory to app/models/user_training_resource/create.rb or lib/user_training_resource/create.rb

Related

Rails / Rspec: Having an anonymous controller be of a certain class

My controllers inherit actions from ApplicationController. My goal is to test the behaviour of any controller that inherits from ApplicationController. I created RandomController in my specs in order to achieve that goal.
Here is my spec so far
require 'rails_helper'
RSpec.configure do |c|
c.infer_base_class_for_anonymous_controllers = false
end
class RandomController < ApplicationController; end
class Random < ApplicationRecord; end
RSpec.describe RandomController, type: :controller do
controller {}
describe '.index' do
context 'when no record exists' do
before { get :index }
specify { should respond_with(200) }
end
end
end
Here is application_controller
class ApplicationController
def index
binding.pry
end
end
The issue is that when the index method runs, self.class returns #<Class:0x00007f8c33b56fc8> instead of RandomController. Is it possible to have my anonymous controller be an instance of a given controller (declared within the specs) ?
According to the docs you can specify the base class for the anonymous controller:
To specify a different base class you can pass the class explicitly to the
controller method:
controller(BaseController)
https://relishapp.com/rspec/rspec-rails/docs/controller-specs/anonymous-controller
Thus you can probably call:
controller(RandomController)
in your specs
Consider using shared_context instead of creating a RandomController to test shared code:
shared_context 'an application controller' do
describe '#index' do
context 'when no record exists' do
before { get :index }
expect(response).to have_http_status(:ok)
end
end
end
You would typically put this file under /spec/support. Example:
/spec/support/shared_contexts_for_application_controllers.rb
Then, in each controller that inherits from ApplicationController:
describe RandomController do
include_context 'an application controller'
end

rspec - Newly instantiated record is nil according to assigns

In my controller:
class CategoriesController < ApplicationController
before_filter :authenticate_super_admin!
def new
#thecategory = Category.new
#thebrands = Brand.all
render "categories/new"
end
end
In my test
require 'rails_helper'
RSpec.describe CategoriesController, type: :controller do
before(:all) do
#the_super_admin = createLoggedInSuperAdmin
end
context "#new" do
it "instantiates new category" do
get :new
expect(assigns(:thecategory)).to be_a_new(Category)
end
end
end
it keeps on telling me expected nil to be a kind of Model and when I inspect it with pry assigns(:themodel) shows as nil
I haven't been able to find any answers that help me with this situation
Turns out I was an idiot, the createLoggedInSuperAdmin call should have been wrapped in before(:each) instead of before(:all), that was why the authentication was working for the first test but failing on the second one

Rspec testing POROs included in models rails

In my Order model I include a PORO class "ShipmentHandler". This PORO is located like this: app/models/order/shipment_handler.rb
I invoke this in my Order model like so:
def assign_shipments
ShipmentHandler.new(self).assign_shipments
end
My PORO looks like:
class Order
class ShipmentHandler
def initialize(order)
#set_some_variables
end
def some_methods
end
end
end
I'm trying to create spec to test the methods in my ShipmentHandler class. I'm not sure how to do this as I keep getting errors like uninitialized constant ShipmentHandler
I've tried to add it to my order_spec.rb like so:
describe Order do
describe Order::ShipmentHandler do
end
end
and:
describe Order do
describe ShipmentHandler do
end
end
Neither work. I've also tried creating a spec in spec/models/order/shipment_handler_spec.rb
This also failed.
The following way of writing specs worked for me when I made some assumptions on what your Order class looks like with the nested ShipmentHandler class:
class Order
def assign_shipments
ShipmentHandler.new(self).assign_shipments
end
class ShipmentHandler
def initialize(order)
#order = order
end
def some_methods
end
end
end
RSpec.describe Order do
it { is_expected.to be_a Order }
end
# Method 1
RSpec.describe Order::ShipmentHandler do
subject(:shipment_handler) { described_class.new(Order.new) }
it { is_expected.to be_a Order::ShipmentHandler }
end
# Method 2
class Order
RSpec.describe ShipmentHandler do
subject(:shipment_handler) { described_class.new(Order.new) }
it { is_expected.to be_a Order::ShipmentHandler }
end
end

Best approach to test scope chains in Rails

In all my ruby on rails app, I try to not use the database in controllers, since they should be independent from persistence classes. I used mocking instead.
Here is the example for rspec and rspec-mock:
class CouponsController < ApplicationController
def index
#coupons = Coupon.all
end
end
require 'spec_helper'
describe CouponsController do
let(:all_coupons) { mock }
it 'should return all coupons' do
Coupon.should_receive(:all).and_return(all_coupons)
get :index
assigns(:coupons).should == all_coupons
response.should be_success
end
end
But what if controller contains more complex scopes, like:
class CouponsController < ApplicationController
def index
#coupons = Coupon.unredeemed.by_shop(shop).by_country(country)
end
end
Do you know any good approach for testing simillar scopes chains?
I think that the following test does not look really good:
require 'spec_helper'
describe CouponsController do
it 'should return all coupons' do
Coupon.should_receive(:unredeemed).and_return(result = mock)
result.should_receive(:by_shop).with(shop).and_return(result)
result.should_receive(:by_country).with(country).and_return(result)
get :index
assigns(:coupons).should == result
response.should be_success
end
end
You could use stub_chain method for that.
Something like:
Coupon.stub_chain(:unredeemed, :by_shop, :by_country).and_return(result)
Just an example.
With rspec > 3 use this syntax:
expect(Converter).to receive_message_chain("new.update_value").with('test').with(no_args)
instead of stub_chain.
Read more about message chains in the documenation.

How do i get rid of #controller is nil error in my tests

I keep getting
#controller is nil: make sure you set it in your test's setup method.
When I run my tests. Any idea what this means?
when you inherit from ActionController::TestCase it infers the controller name from the test name if they do not match you have to use the setup part of test to set it.
So if you have
class PostsControllerTest < ActionController::TestCase
def test_index
#assert something
end
end
Then #controller is auto instantiated to PostsController, however, if this were not the case and you had a different name you would need a setup as such
class SomeTest < ActionController::TestCase
def setup
#controller = PostController.new
end
end
I was in the process of upgrading to rspec 3 from the beta version on rails 4 and ran into this error. The problem turned out to be that our Controller spec describe statements used symbols instead of strings. Rspec was attempting to instantiate the symbol as the controller but they were in fact 'actions'.
#trys to set #controller = Index.new
describe SomeController do
describe :index do
before do
get :index, format: :json
end
it { expect(response).to be_success}
end
end
#works
describe SomeController do
describe 'index' do
before do
get :index, format: :json
end
it { expect(response).to be_success}
end
end
ErsatzRyan answer is correct, however there is a small typo. Instead of
#controller = PostsController
it should be
#controller = PostsController.new
otherwise you get an error: undefined method `response_body='
Check whether you are ending the do and end properly.
RSpec.describe LeadsController, type: :controller do
# All tests should come here
end
If the names match, and the #controller variable is still nil, try checking for errors in the controller instantiation. For me I had a controller initialize method that had a bug in it. For some reason the controller was just nil in the test, rather than throwing an error when it wasn't instantiated.
Or, you can simply do this:
RSpec.describe PostsControllerTest, :type => :controller do
# ...
end
In Rails 6 looks like it is like this:
tests PostController
https://www.rubydoc.info/docs/rails/4.1.7/ActionController/TestCase#label-Controller+is+automatically+inferred
I encountered this error because I surrounded the controller name in quotes.
# broken
RSpec.describe 'RegistrationsController', type: :controller do
...
end
# works
RSpec.describe RegistrationsController, type: :controller do
...
end

Resources