Rails engine testing controller using helper method of main app in rspec - ruby-on-rails

I have one main rails app, and I have an engine within it, now the engine has a different test, I need to test one controller which is using main app MailerHelper module. How can I access in my spec.
My engine's user controller
module ControlPanel
class UserController < ApplicationController
include MailerHelper
def index
#users = User.all
end
end
end
I am using MailerHelper have many other methods which sent mails. As this is controller is in my engine control panel, following is specification in my engine
require 'rails_helper'
describe ControlPanel::UsersController, type: :controller do
describe '#index' do
it 'should display all users' do
get :index
expect(response).to render_template('index')
end
end
end
The test case is just simple, but as in my engines User controller I am using my main application helper, so it can't take it, I have seen many replies but nothing working, like including in rails_spec and ::MailerHelper etc.

Related

Anonymous controllers in Rails

I am currently learning to test with rspec (ruby on rails) (it's safe to assume I know very little), and am a bit stuck when it comes to testing with anonymous controllers. I tried googling it, but I feel like it doesn't give me what I'm looking for. So basically, what is an anonymous controller and why (or when) do I want one?
Thanks in advance
The main reason you would want to use an anonymous controller is that you are developing reusable library code (a gem) thats meant to plug into any arbitrary controller.
For example this base controller class to dry the common CRUD boilerplate code:
module MyGem
class ResourcefulController < ::ActionController::Base
before_action :set_resource, only: [:show, :edit, :update, :destroy]
def show
end
private
def derive_resource_class_name
self.class.name.demodulize.chomp('Controller').singularize
end
def resource_class
derive_resource_class_name.constantize
end
def set_resource
#resource = resource_class.find(params[:id])
end
end
end
The normal way of testing this would require that we create a controller and a route just for the test itself.
By using an anonymous controller we can get around that:
require 'rails_helper'
RSpec.describe MyGem::ResourcefulController, type: :controller do
controller do
def show
render plain: #resource
end
end
before do
model = Class.new do
def self.find(id)
"You found me"
end
end
stub_const("Resourceful", model)
end
it "derives the resource class from the name of the controller" do
get :show, params: { id: 1 }
expect(response.body).to eq "You found me"
end
end
RSpec also cleverly creates stub routes for the normal CRUD actions.
But for normal Rails application code its not very useful at all - parent classes (or included modules) are better tested by creating a request or feature spec that covers the actual behaviour in the end product. This lets you create a failing spec, get to green and then refactor.

How to test the AdminController when it has no actions?

My AdminController looks like:
class AdminController < ApplicationController
before_action :check_admin
private
def check_admin
redirect_to 'home/error' unless current_user.admin?
end
end
In my rspec test, how can I test this if there are no route or views?
require 'rails_helper'
RSpec.describe AdminController, type: :controller do
context "with no render_views" do
it "redirects for non-admin users" do
#???expect do
end
end
end
I am assuming that you are using a before_action in your AdminController, even though this controller does not have any actions, so that any controllers that inherit from it will automatically by "admin only".
If so, there are two ways to approach testing this.
1) Don't write a test for check_admin.
Instead, write tests for any controller actions that you define later! For example, if you have the following controller in your application tomorrow:
UsersController < AdminController
def index
#users = User.all
end
end
then you can write the following specs for that controller.
describe UsersController
it 'redirects for non-admins' do
# insert the test you feel like writing here!
end
it 'renders the right template for admin users' do
magical_login_method
get :index
expect(response).to render_template(:index)
end
end
and so on!
2) Call the private method directly
This approach makes me feel a bit icky. Although this defeats the philosophy of public vs private methods, you can call a private method in ruby by using the .send method.
describe AdminController
it 'redirects for non-admins' do
# make an instance of your controller
controller = AdminController.new
# expect the controller to call `redirect_to`
expect(controller).to receive(:redirect_to).with('home/error')
# call the private `check_admin` method
controller.send(:check_admin)
end
end
Some, perhaps many, would argue that this sort of testing is highly intrusive, and may even limit the flexibility of your codebase in the future. I'd recommend approach 1, not because it's lazy, but because it tests things once there's something to test!

How to stub a method from a module in a controller?

lib/modules/api.rb:
module Api
require 'net/http'
require 'json'
def send_get_request(url, body)
# some logic
end
end
The controller:
class DashboardController < ApplicationController
include Api
def index
response = send_get_request(_some_, _params_)[:json]
#something = response['something']
end
end
How do I stub the send_get_request method? I tried a feature test:
require 'rails_helper'
describe 'visiting users page'
it 'shows users page' do
visit '/'
allow_any_instance_of(Api).to receive(:send_get_request).with(any_args).and_return({json: {'total_paying' => '3'}})
within('#side-menu') do
click_link 'Users'
end
expect(page).to have_selector('.page-header', text: 'Users')
end
end
but apparently it doesn't work (the test fails because real send_get_request method gets called with params that are incorrect (which they are in tests).
When I tried
expect_any_instance_of(Api).to receive(:send_get_request).once
That expectation passed.
Well, it's the controller that will receive the message. The test, as a client, does not care about how the method is defined, "normally" or mixed-in.
If it were a controller spec, you'd be able to do this:
allow(controller).to receive(:send_get_request).with(...)
As for the feature specs, don't stub anything in there. It's supposed to be the "real" interaction. In feature specs you use capybara (or something) to fill the forms and then check the database (to see if a user got created, etc.)
To shield your feature specs from external api, you can use VCR gem. It basically runs your external query one time, writes the response into a file and then, on subsequent runs, just plays it back, without contacting external api again.

Rails: how to write an anonymous spec controller to test authorisation

I have a controller, AdminController, which sets the various authorisation levels for the rest of the CMS. Because there are no controller actions, just methods, I began to research ways to test these against controllers.
The conclusion I came to was that they needed to be tested independently of the controllers they are used in (I want to to steer clear of integration testing if possible, like capybara etc).
I found some articles like this one to help me along.
So far I have written this spec which is failing with the errors below. I am not sure about it to be honest and wanted to here what SO community had to say on what I am trying to achieve.
describe AdminController do
controller do
before_filter :authorize_fixture_uploader!
def index
render text: 'Hello World'
end
end
let(:admin){FactoryGirl.create(:admin)}
describe "authentication" do
before do
sign_in admin
allow(controller).to receive(:current_admin).and_return(admin)
end
describe "authorize_fixture_uploader! helper" do
context "signed in" do
before do
allow(:admin).to receive(:authorize_fixture_uploader!).and_return(false)
get :index
end
it "redirects do admin_home_path" do
expect(response).to redirect_to admin_home_path
end
end
end
end
end
and here is the controller
class AdminController < ApplicationController
before_filter :authenticate_admin!
def authorize_fixture_uploader!
unless current_admin.fixture_uploader?
return redirect_to(admin_home_path)
end
end
end
This test is giving me the error
1) AdminController authentication authorize_fixture_uploader! helper signed in redirects do admin_home_path
Failure/Error: allow(:admin).to receive(:authorize_fixture_uploader?).and_return(false)
TypeError:
can't define singleton
I am worried its because my whole approach to this is wrong. Help would most certainly be appreciated.
Updated thanks to #blelump's answer.
I had a type which was causing the first issue. But Now I am getting error
undefined method `authorize_fixture_uploader?' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_1::Nested_1::Nested_1:0x007f9357857108>
The logic behind this i throwing me a bit. How am I to test these methods independent of the controllers they are used?
You have a typo:
allow(:admin).to receive(:authorize_fixture_uploader!).and_return(false)
Now you're trying to add authorize_fixture_uploader! to Symbol. Just start with controller variable:
allow(controller).to receive(:authorize_fixture_uploader!).and_return(false)
Aside from the poor attention detail highlight by blelump above, the real flaw in my approach was the lack of routes. I found a very useful article from pivotal labs http://pivotallabs.com/adding-routes-for-tests-specs-with-rails-3/ which saved the day.
Read the article, but it essentially boils down to this.
require 'spec_helper'
class InheritsFromAdminController < AdminController
def show
render :text => "foo"
end
end
describe InheritsFromAdminController do
before do
Rails.application.routes.draw do
# add the route that you need in order to test
match '/foo' => "inherits_from_admin#show"
# re-drawing routes means that you lose any routes you defined in routes.rb
# so you have to add those back here if your controller references them
match '/login' => "sessions/new", :as => login
end
end
after do
# be sure to reload routes after the tests run, otherwise all your
# other controller specs will fail
Rails.application.reload_routes!
end
it "requires logged-in users" do
get :show
response.should redirect_to("/login")
end
end

post to a different controller in an rspec test

How do I post to a different controller than the one the test script is currently pointing at?
Example:
in user_controller_spec.rb
it "should just post to users" do
post :create, #params # this goes to the users controller
end
I want to do something like:
it "should post to user and people to do some integration testing" do
post :create, #params # this goes to the users controller still
post 'people', :create, #params # this goes to the people controller
end
ps: i don't want to setup cucumber
Controller specs are wrappers for Rails functional tests, which don't support multiple requests or controllers. What you want is an RSpec request spec (rails 3) or an integration spec (rails 2). These wrap Rails integration tests, which does support multiple requests with multiple controllers (multiple sessions, even), but they work a bit differently from controller specs. You have to use the full path (so get new_thing_path), and you can't stub anything on the controller (because there is no controller before you make a request).
See http://relishapp.com/rspec/rspec-rails/docs/request-specs/request-spec and http://api.rubyonrails.org/classes/ActionDispatch/IntegrationTest.html for more info.
There is a way if you assigning the value of #controller before call test method. Example
def setup
#controller = UserController.New
do user stuff
#controller = ThisController.New
do test intented for this controller
end
Based on other answer, but more safe.
Store current controller instance and create a new with the required controller. Finally, replace new controller instance with the old stored instance.
def setup
old_controller = #controller
#controller = UserController.new
# do user stuff
#controller = old_controller
end

Resources