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