Hi fellow rubyists,
I have somewhat simple hanami action, smth like:
class MyAction
extend Dry::Initializer
option :repo, default: -> { Repos::MyRepo }
params do
....
end
def handle(req, res)
res.body = repo.some_method(req.params[:id]).to_json
end
end
Now, for testing, i am initialising this action,
subject do
described_class.new(repo: repo_double)
.call(params)
end
With that, i am getting an exception:
Failure/Error:
described_class.new(repo: repo_double)
.call(params)
NoMethodError:
undefined method `handled_exceptions' for nil:NilClass
config.handled_exceptions.each do |exception_class, handler|
^^^^^^^^^^^^^^^^^^^
# /usr/local/bundle/gems/hanami-controller-2.0.0.rc1/lib/hanami/action.rb:437:in `exception_handler'
# /usr/local/bundle/gems/hanami-controller-2.0.0.rc1/lib/hanami/action.rb:487:in `_handle_exception'
# /usr/local/bundle/gems/hanami-controller-2.0.0.rc1/lib/hanami/action.rb:346:in `rescue in block in call'
# /usr/local/bundle/gems/hanami-controller-2.0.0.rc1/lib/hanami/action.rb:325:in `block in call'
# /usr/local/bundle/gems/hanami-controller-2.0.0.rc1/lib/hanami/action.rb:324:in `catch'
# /usr/local/bundle/gems/hanami-controller-2.0.0.rc1/lib/hanami/action.rb:324:in `call'
Looking at the docu, standalone testing seems to require configuration object.
I am not sure what is exactly expected here. Hanami::Controller::Configuration (smth mentioned in the docs) does not seem to exist, and passing configuration: Hanami::Action.config as parameter to action initialisation does not seem to help either.
What I might be missing here to unit test the action ?
P.S. beta4 did not crash this way, I was simply initialising action with dependencies and invoking .call with hash of params.
# app/repositories/my_repo.rb
# frozen_string_literal: true
module Bookshelf
module Repositories
class MyRepo
end
end
end
# app/actions/books/index.rb
# frozen_string_literal: true
module Bookshelf
module Actions
module Books
class Index < Bookshelf::Action
# THIS IS A REPLACEMENT FOR EXPLICIT dry-initializer
include Deps["repositories.my_repo"]
def handle(*, response)
response.body = self.class.name
end
end
end
end
end
# spec/actions/books/index_spec.rb
# frozen_string_literal: true
RSpec.describe Bookshelf::Actions::Books::Index do
subject { described_class.new(my_repo: repo) }
let(:params) { Hash[] }
let(:repo) { double("repo") }
it "works" do
response = subject.call(params)
expect(response).to be_successful
end
end
Explanation: They keyword argument to pass is the “local name” of the import:
include Deps["repositories.my_repo"]
It requires my_repo: keyword argument
You can also be more explicit and use:
include Deps[my_repo: "repositories.my_repo"]
or
include Deps[foo: "repositories.my_repo"]
Which will require my_repo: and foo: keyword arguments, respectively.
Thanks @jodosha for your reply.
I am not using full hanami framework, only router and actions. Plus, my app structure is somewhat unusual ( i am not registering classes, used in actions as providers)
Therefore I believe I do not have all the inflectors available.
Is there a way to use my own DI for actions, and test them standalone ?
Just a quick note here, Action’s default initialiser looks like this:
# Returns a new action
#
# @since 2.0.0
# @api public
def initialize(config: self.class.config)
@config = config
freeze
end
So you shouldn’t actually have to pass in a config; it’ll use the class-level config by default.
I think a problem here is that Hanami::Action in no way expects dry-initializer to be present, so if you want dry-initializer’s own initialize to run, you’d have to make sure you invoke it from within your own #initialize in your action base class, something like this:
class MyBaseActionClass < Hanami::Action
def initialize(...)
super()
__dry_initializer_initialize__(...)
end
end
Both Controller::Configuration (smth mentioned in docs) and what you @jodosha suggested (Hanami::Action::Configuration) seem to be not available. Are they coming from some other mixin, that i might be missing ?
I have hanami controller, router, validations, helpers in my gemfile, and utils is being fetched as dependency for controller.
It seems indeed like starting from rc1 dry initializer injection is not supported. And even overriding initialiser in BaseAction, like @timriley is suggesting, does not seem to solve the issue, config still appears to be nil when invoking action within unit test.