Rails and adapter objects: different implementations in production and tests
… and check why 5600+ Rails engineers read also this
Rails and adapter objects: different implementations in production and tests
If you work with service objects in Rails apps, very quickly you need to have the dependency being passed to the service object constructor. Which usually means, that the Rails controller needs to do it. This blogpost describes how to have a different implementation being passed in the production environment and an in-memory one in the tests.
There are several possible solutions, but one could be closer to the hearts of many Rails developers. Let’s just use the built-in Rails environments and configure them appropriately:
$ ag foo_adapter config/environments/
config/environments/development.rb
187: config.foo_adapter = FooAdapter.new
config/environments/production.rb
164: config.foo_adapter = FooAdapter.new
config/environments/test.rb
184: config.foo_adapter = InMemoryFooAdapter.new
As you see, we have the same implementations in the production/dev environments, but a different one in the tests. The tests use an in-memory implementation which probably doesn’t really send the requests to the Foo API.
In the Rails controller, you can then initialize the service object with:
def create
RegisterNewUser.new(Rails.application.config.foo_adapter).call
#...
end
while in the service object you stay unaware of the difference:
class RegisterNewUser
def initialize(foo_adapter)
@foo_adapter = foo_adapter
end
end