Loading providers for specs

Over in a GitHub issue, @vladimirtcats asked this:

I have created provider:

Hanami.app.register_provider :serializers do
  prepare do
    require "oj"
    require "alba"
  end

  start do
    Alba.backend = :oj
  end
end

But I can’t use Alba and oj in specs

only after requiring this code in spec/spec_helper.rb

require "oj"
require "alba"

# or
Hanami.boot

it works

How can I do it correctly?


Firstly, thanks for trying out different things with Hanami! This is a good question, so I thought I’d bring an answer over here into the forum for better visibility :slight_smile:

So Hanami’s default RSpec setup does not ever fully boot the app: Hanami.boot or require "hanami/boot" do not feature anywhere in spec/spec_helper.rb or the other files it loads.

This is by design — by avoiding a full app boot, it helps your individual specs run as fast as possible, because they only load the components from the app that they actually need.

However, in the case of a provider like yours, or basically any bit of code that won’t be automatically loaded as part of your tests interacting with one of your classes or with the app in general, you’ll need to do the work to start that provider explicitly.

In your case, this could be as simple as adding something like this to your spec_helper.rb:

Hanami.app.start :serializers

This would ensure that the oj and alba gems are loaded and configured appropriately before any of your tests run.

Another approach could be to start that provider the first time you encounter an example with a specific tag, e.g.

RSpec.configure do |config|
  config.before :each, :serializer do
    Hanami.app.start :serializers
  end
end

Then you’d just need to remember to add the tag to your examples, e.g. RSpec.describe "my serializer", :serializer do ... end.

Or you could do even fancier things on top of this:

RSpec.configure do |config|
  config.define_derived_metadata(file_path: %r{/serializers/}) do |metadata|
    metadata[:serializer] = true
  end
end

This would automatically add the :serializer metadata to any file inside a /serializers/ dir within your tests, so you don’t need to remember to add it yourself.

So you have a few options, you can pick the one which feels best to you :slight_smile:


One more idea: because your provider is actually not very complex, the other way you can ensure that that code is always loaded appropriately could be to leverage a serializer superclass. If all your app’s serializer’s inherited from a common superclass, then you could add the three lines from your provider to the file defining that superclass:

require "oj"
require "alba"
Alba.backend = :oj

module MyApp
  class BaseSerializer
    # ^ then your serializers inherit from that
  end
end

This matches the pattern we use for base action, view, etc. classes within Hanami apps. Those base classes serve as a place where any critical config/setup code can exist, ensuring it is always loaded before any subclasses are defined.

I hope this helps!

1 Like

thanks you! it will help me to organize dependencies in my code better)

1 Like

I just wrote something very similar:

RSpec.configure do |config|
  config.before :each, :start do |example|
    example.metadata => { start: }

    containers = [Hanami.app.container] + Hanami.app.slices.each.map(&:container)

    Array(start).each do |provider_name|
      containers.each do |system|
        if system.providers.find_and_load_provider(provider_name)
          system.start(provider_name)
        end
      end
    end
  end
end

So it’s more general purpose, it would allow:

RSpec.describe "myserializer", start: :serializers do
3 Likes