How to fake out a subdomain lookup in Rails tests? - ruby-on-rails

I have the following filter defined:
# application_controller.rb
class ApplicationController < ActionController::Base
before_filter :find_account
private
def find_account
#current_account = Account.find_by_subdomain!(request.subdomains.first)
end
end
and in my test:
# users_controller_test.rb
class UsersControllerTest < ActionController::TestCase
setup do
#request.host = "test.myapp.local"
end
# ...
end
Now test is defined as the subdomain for a dummy account that I load prior to all requests using factory_girl. However, this is throwing a nil object error, saying that #request is nil. Removing the setup block causes all of my tests to fail as find_account cannot find an account and therefore throws a RecordNotFound error.
What am I doing wrong?

Try this:
#request.env['HTTP_HOST'] = 'test.myapp.local'

Related

Facing the NameError when trying to include concerns in my rails 6 controller

I am trying to create a small project for practising the API Key Authentication using the this tutorial. I have created a concern in following directory of the rails project.
app/controllers/concerns/api_key_authenticable.rb
module ApiKeyAuthenticatable
extend ActiveSupport::Concern
include ActionController::HttpAuthentication::Basic::ControllerMethods
include ActionController::HttpAuthentication::Token::ControllerMethods
attr_reader :current_api_key
attr_reader :current_bearer
# Use this to raise an error and automatically respond with a 401 HTTP status
# code when API key authentication fails
def authenticate_with_api_key!
#current_bearer = authenticate_or_request_with_http_token &method(:authenticator)
end
# Use this for optional API key authentication
def authenticate_with_api_key
#current_bearer = authenticate_with_http_token &method(:authenticator)
end
private
attr_writer :current_api_key
attr_writer :current_bearer
def authenticator(http_token, options)
#current_api_key = ApiKey.find_by token: http_token
current_api_key&.bearer
end
end
In my Controller, I am trying to include the concern like this
app/controllers/ApiKeysController.rb
class ApiKeysController < ApplicationController
include ApiKeyAuthenticatable
# Require token authentication for index
prepend_before_action :authenticate_with_api_key!, only: [:index]
# Optional token authentication for logout
prepend_before_action :authenticate_with_api_key, only: [:destroy]
def index
end
def create
end
def destroy
end
end
But when I run the server and visit the index action of the controller, I get the following error
NameError (uninitialized constant ApiKeysController::ApiKeyAuthenticatable):
app/controllers/ApiKeysController.rb:10:in `<class:ApiKeysController>'
app/controllers/ApiKeysController.rb:2:in `<main>'
Can anybody help me out to fix this issue?
Author here. Your concern’s filename is authenticable but your module is authenticatable. You’ll need to correct the typo in the concern filename.

Rspec Passes when all tests are run, but fail when individual tests are run

I noticed something strange in my rspec testing.
When I run all my rspec tests, my tests that rely on my require "plan_access_control" statement pass, for example, AController, BController, and CController all pass.
However, if I run BController_spec.rb separately, it fails. How do I get my tests to run in isolation to each other so they should fail? My assumption is that once the require gets loaded, it's loaded for all tests, but this should not happen.
# controllers/concerns/access_controllable.rb
module AccessControllable
extend ActiveSupport::Concern
require "plan_access_control"
def validation_object
#validation_object ||= PlanAccessControl::Validations.new(counts: 1)
end
end
#lib/plan_access_control.rb
module PlanAccessControl
autoload :Validations, "plan_access_control/validations"
end
# lib/plan_access_control/validations.rb
module PlanAccessControl
class Validations
def initialize(counts:)
self.counts = counts
end
end
end
# controller/a_controller.rb
class AController < ApplicationController
include AccessControllable
def new
#a = PlanAccessControl::Validations.new(counts: 1)
end
end
# controller/b_controller.rb
class BController < ApplicationController
def new
#b = PlanAccessControl::Validations.new(counts: 1)
end
end
# controller/c_controller.rb
class CController < ApplicationController
include AccessControllable
def new
#c = PlanAccessControl::Validations.new(counts: 1)
end
end

Rails Functional Test: How to test Admin::PostsController

I have a controller like follows:
namespace :admin do
resources :posts
end
# app/controllers/admin_posts_controller.rb
module Admin
class PostsController < ApplicationController
end
end
The problem is I don't know where Admin::PostsControllerTest goes.
I was expecting something like following works:
# test/functional/admin/posts_controller_test.rb
module Admin
class PostsControllerTest < ActionController::TestCase
end
end
But if I do that I get the following error when I run rake test:functionals :
RuntimeError: #controller is nil: make sure you set it in your test's setup method.
You've got the right location. Try this:
class Admin::PostsControllerTest < ActionController::TestCase
#put your tests here
end

How are both of these tests passing?

I'm using Shoulda, Mocha, and Test::Unit for testing. I have the following controller and test:
class FoosController < ApplicationController
caches_action :show
def show
#foo = requested_foo
end
private
def requested_foo
Foo.find(params[:id])
end
end
class FoosCachingTest < ActionController::IntegrationTest
def setup
#foo = Foo.first
#session = open_session
end
context 'FoosController#show' do
setup do
#session.get('/foos/1')
end
before_should 'not fetch the Foo from the database' do
FoosController.any_instance.expects(:requested_foo).never
end
before_should 'fetch the Foo from the database' do
FoosController.any_instance.expects(:requested_foo).once.returns(#foo)
end
end
end
How is it that both of those tests could pass? I am not explicitly expiring my mocks at any point. Are Mohca and Shoulda known to interact poorly in this regard?
Aha! The problem is that the expectations do indeed fail but the errors they throw are swallowed up by the controller's error handling. Testing that the action renders properly makes the proper test fail.

How do you test a Rails controller method exposed as a helper_method?

They don't seem to be accessible from ActionView::TestCase
That's right, helper methods are not exposed in the view tests - but they can be tested in your functional tests. And since they are defined in the controller, this is the right place to test them. Your helper method is probably defined as private, so you'll have to use Ruby metaprogramming to call the method.
app/controllers/posts_controller.rb:
class PostsController < ApplicationController
private
def format_something
"abc"
end
helper_method :format_something
end
test/functional/posts_controller_test.rb:
require 'test_helper'
class PostsControllerTest < ActionController::TestCase
test "the format_something helper returns 'abc'" do
assert_equal 'abc', #controller.send(:format_something)
end
end
This feels awkward, because you're getting around encapsulation by using send on a private method.
A better approach is to put the helper method in a module in the /controller/concerns directory, and create tests specifically just for this module.
e.g. in app controller/posts_controller.rb
class PostsController < ApplicationController
include Formattable
end
in app/controller/concerns/formattable.rb
module Concerns
module Formattable
extend ActiveSupport::Concern # adds the new hot concerns stuff, optional
def format_something
"abc"
end
end
end
And in the test/functional/concerns/formattable_test.rb
require 'test_helper'
# setup a fake controller to test against
class FormattableTestController
include Concerns::Formattable
end
class FormattableTest < ActiveSupport::TestCase
test "the format_something helper returns 'abc'" do
controller = FormattableTestController.new
assert_equal 'abc', controller.format_something
end
end
You could test #controller.view_context from your functional/controller tests. This method is available in Rails 3, 4, and 5, as far as I can tell.
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
helper_method :current_user
# ...
end
test/controllers/application_controller_test.rb
require 'test_helper'
class ApplicationControllerTest < ActionController::TestCase
test 'current_user helper exists in view context' do
assert_respond_to #controller.view_context, :current_user
end
end
If you didn't want to test one of your controller subclasses, you could also create a test controller to verify that the method in the view_context is the same one from the controller and not from one of your view helpers.
class ApplicationControllerHelperTest < ActionController::TestCase
class TestController < ApplicationController
private
def current_user
User.new
end
end
tests TestController
test 'current_user helper exists in view context' do
assert_respond_to #controller.view_context, :current_user
end
test 'current_user returns value from controller' do
assert_instance_of User, #controller.view_context.current_user
end
end
Or, more likely, you'd want to be able to test the helper in the presence of a request.
class ApplicationControllerHelperTest < ActionController::TestCase
class TestController < ApplicationController
def index
render plain: 'Hello, World!'
end
end
tests TestController
def with_routing
# http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-with_routing
# http://guides.rubyonrails.org/routing.html#connecting-urls-to-code
super do |set|
set.draw do
get 'application_controller_test/test', to: 'application_controller_test/test#index'
end
yield
end
end
test 'current_user helper exists in view context' do
assert_respond_to #controller.view_context, :current_user
end
test 'current_user returns value from controller' do
with_routing do
# set up your session, perhaps
user = User.create! username: 'testuser'
session[:user_id] = user.id
get :index
assert_equal user.id, #controller.view_context.current_user.id
end
end
end
I've struggled with this for a bit, because the accepted answer didn't actually test whether the method was exposed as a helper method.
That said, we can use the #helpers method to get a proxy for testing.
For example:
class FooController < ApplicationController
private
def bar
'bar'
end
helper_method :bar
end
Can be tested with:
require 'test_helper'
class FooControllerTest < ActionController::TestCase
test 'bar is a helper method' do
assert_equal 'bar', #controller.helpers.bar
end
end
Indeed they're not. The view tests are specifically for the views. They don't load the controllers.
You should mock this method and make it return whatever is appropriate depending of your context.

Resources