Dynamically add method in before(:all) - ruby-on-rails

Im testing a Module that can be included in a Controller.
Right now the code looks like that:
class GithubAuthorizationController < ApplicationController
include Frontend::Concerns::GithubAuthorization
def index
render inline: "lorem_ipsum"
end
end
describe GithubAuthorizationController do
before(:all) do
#page_content = "lorem_ipsum"
end
...........
As you can see I basically create a Test-Controller before the tests are run. Now I would like to add the module and index method in the before(:all)-block. I tried:
class GithubAuthorizationController < ApplicationController
end
describe GithubAuthorizationController do
before(:all) do
#page_content = "lorem_ipsum"
class < #controller
include Frontend::Concerns::GithubAuthorization
def index
render inline: "lorem_ipsum"
end
end
end
...........
As I can see in debugger in the before(:all) block the #controller is defined as <GithubAuthorizationController .... So It is a instance. There Is also no error when running the code, but the tests fails, because of The action 'index' could not be found ...
What do I wrong? How can I move the code to the before(:all) block? Thanks

The way to do this in rspec is with a controller block:
describe GithubAuthorizationController, type: :controller do
context "with module" do
controller do
include Frontend::Concerns::GithubAuthorization
def index
render inline: "lorem_ipsum"
end
end
# within this block the controller will be the anonymous controller class created above
end
end
If you have set infer_base_class_for_anonymous_controllers to false (this is not the default) then you need to do controller(GithubAuthorizationController) or you'll inherit directly from ApplicationController
Your issue could be down to a missing route - the controller helper creates some routes for you (for the default index, show etc.) actions. You can add extra ones in an example with
it "does something with a custom route" do
routes.draw { get "custom" => "github_authorization#custom" }
get :custom
...
end

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

with_routing test helper doesn't work for integration tests

Simple example that by all accounts should work:
require 'test_helper'
class FoosController < ApplicationController
def index
render plain: 'something'
end
end
class UsersControllerTest < ActionDispatch::IntegrationTest
def test_some_routing
with_routing do |set|
set.draw do
get '/foos' => 'foos#index'
end
get '/foos'
assert_equal 200, response.status
end
end
end
Instead I'm getting: ActionController::RoutingError: No route matches [GET] "/foos"
What am I doing wrong? This is latest Rails 4.1 btw.
For those who come here seeking a solution to test their controller concerns in isolation (like I did), it may pay to use setup and teardown blocks instead of littering your code with with_routing blocks:
require 'test_helper'
class ConcernedController < ApplicationController
include Concern
def action
render plain: "response", status: :ok
end
end
class ConcernTest < ActionDispatch::IntegrationTest
setup do
#controller = ConcernedController.new
Rails.application.routes.draw do
get "/action" => "concerned#action"
post "/action" => "concerned#action"
end
end
teardown do
Rails.application.reload_routes!
end
test "concern method" do
post "/action"
# Test what the concern does
assert_response :ok
assert_equal "Response", response.body
end
end
Tested against Rails 5.0.2.
Ok, so I'm answering my own questions again. Problem is that with_routing doesn't work at all inside integration tests. Oversight or something, not sure. It only works for controller tests. So here's the work around:
class ActionDispatch::IntegrationTest
def with_routing(&block)
yield ComfortableMexicanSofa::Application.routes
ensure
ComfortableMexicanSofa::Application.routes_reloader.reload!
end
end
Basically you will redefine your application routes within that block and then reload them back from the routes.rb file.
-- edited --
We can have a block with new routes by defining a new method like:
def with_foo_route
Rails.application.routes.draw do
get 'foo' => 'foo#index'
end
yield
Rails.application.routes_reloader.reload!
end
And then execute a get requests to this new route by:
with_foo_route do
get '/foo'
assert_equal 200, status
end

Testing sub application controller

I have a base admin controller that inherits from ApplicationController. In order to test a before filter in Admin::BaseController, I created an anonymous controller in this spec.
require 'spec_helper'
describe Admin::BaseController do
it { should be_a(ApplicationController) }
controller do
def index
render :text => ''
end
end
context 'when current user is not an admin' do
it 'redirects to root path' do
get :index
response.should redirect_to(root_path)
end
end
end
But when I make a request for index action, it doesn't call the before filter in Admin::BaseController.
When I define that filter in ApplicationController instead of Admin::BaseController and run the test, it works. Apparently this anonymous controller inherits from ApplicationController. How can I change this behavior?
I've found the answer here; https://www.relishapp.com/rspec/rspec-rails/docs/controller-specs/anonymous-controller
controller(Admin::BaseController) do
def index
render :text => ''
end
end
did the trick.

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