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: