In order to skip the Pundit authorization within controllers you need to stub
out the Policy class' new method to return a placeholder / mock object that
responds with true for the action policy in the action that is being tested;
e.g. for FooController my FooPolicy returns a new instance of itself that
returns true when its action policy index? is called within the index
controller action.
For Minitest it looks something like this
# - Minitest -
foo_policy = MiniTest::Mock.new
foo_policy.expect :index?, true
FooPolicy.stub :new, foo_policy do
# your controller action test
end
You will create a mock object that will fulfil the role of Policy's new instance
object within the Controller action, and return that mock object if the Policy's
new method is called within the scope of its block during a test.
If you using Mocha, I believe the following will work also to achieve the same
as above, but without the need to specific the test assetion within its block.
# - Mocha -
foo_policy = mock("foo_policy")
foo_policy.expects(:index?).returns(true)
FooPolicy.any_instance.stubs(:new).returns(foo_policy)
Also, you can achieve a similar mock object with mock = Object.new and stub
method by defining a method for the mock inline like def mock.index?. This
also applies to existing classes, since Ruby allows you to redeclare a method
for a Class inline like so def FooPolicy.new and only persists within the
scope of the test. Knowing this we can have def FooPolicy.new and have return
our mock like in the following
# - Without a Test-framework -
foo_policy = Object.new # create a mock / double
def foo_policy.index?; true; end # stub a method
def FooPolicy.new # stub Policyclass to return double with stubbed method
foo_policy = Object.new
def foo_policy.index?; true; end
foo_policy
end
For completeness, I use RSpec and use the folllowing to stub out my Pundit
Polices within my controller actions
# - Rspec -
foo_policy = instance_double("policy", index?: true)
allow(FooPolicy).to receive(:new).and_return(foo_policy)
As for Devise I use the following support test helpers and only include them for
Controller and Requests tests that require an User to sign_in before a test:
module DeviseRequestSpecHelpers
include Warden::Test::Helpers
def sign_in(resource_or_scope, resource = nil)
resource ||= resource_or_scope
scope = Devise::Mapping.find_scope!(resource_or_scope)
login_as(resource, scope: scope)
end
def sign_out(resource_or_scope)
scope = Devise::Mapping.find_scope!(resource_or_scope)
logout(scope)
end
end
See Module: Warden::Test::Helpers — Documentation for hassox/warden (master)
for more information on Warden::Test::Helpers module
This is because Warden is used within Devise that its Test Helpers are used.
This Warden::Test::Helpers module when included switches Warden into its
test_mode! and its login_as method will set your user object as the
current_user for the purposes of testing with Devise being used in your
application.
References: