I’m defining a boundary between the delivery mechanism (web) and the business logic. I want to test the delivery mechanism in isolation, and to achieve that I’m stubbing the business logic.
Given the following code on an endpoint:
post '/people' do
Operations::People::CreatePerson.new.call(declared(params)) do |m|
m.success do |person|
status 201
person
end
m.failure do |errors|
status 422
errors
end
end
end
The following operation:
module Operations
module People
class CreatePerson
include Dry::Matcher.for(:call, with: ComplexMatcher)
include Operations::Result::Mixin
def call(person_attributes)
person = Person.create!(person_attributes)
Success(value: person)
rescue ActiveRecord::ActiveRecordError => error
Failure(value: error.message, code: :not_created)
end
end
end
end
I know this isn’t a specific answer for your situation, but the general answer is dependency injection. The controller is given the operation as an object, and that object can be easily mocked out. That’s the idea behind dry-auto_inject.
For your particular situation, you could stub out Operations::People::CreatePerson.new to return a mock, and then have a second stub for call on that mock. Another option would be to wrap the call to the operation in another method – something like:
Yeah, I was half way through writing something and @tom_dalling pretty much covered everything I would’ve said
I think it’d be fair to consider any_instance-style RSpec code to be an indication of some sort of design smell, which is in this case accessing class constants in place rather than passing around instances of objects that are easier to replace with test doubles.
It looks like you’re trying to test something that appears in your routes? Perhaps this’d be better as an integration test anyway, where you don’t replace any parts of your code with mocks?
Anyway, this is one of the reasons in dry-web apps we combine a dry-component container with the Roda app, so we’re dealing with objects in the routes instead of concrete classes, and we keep the option (via dry-container’s stubs support) of stubbing the container for various registered objects if we really needed.