trailblazer / trailblazer-operation

Trailblazer's Operation implementation.
https://trailblazer.to/2.1/docs/operation.html
87 stars 24 forks source link

Stubbing of nested operations #32

Open apotonick opened 5 years ago

apotonick commented 5 years ago

Supposedly many people want to stub nested OPs/ACTs.

 expect(MyNestedOperation). to receive(:call).with("params" => {order_id: order.id, email: order.user.email)

We should explain how to do that in 2.1

fernandes commented 5 years ago

actually, I call the nested inside an step and stub MyNestedOperation.call is the better I can do atm hehehhe

emaglio commented 5 years ago

I do expect(MyNestedOperation).to receive(:call_with_circuit_interface).with(blabla).and_call_original which works fine.

The "real" problem is when you have a series of complex Nested operations and you simply want to test that separately so you simply want to stub a Nested operation and return a "usable" result object so instead to do and_call_original I want to say and_return(my_return_object).

I know that you can pass specific params to make that fail but it would be just quicker/easier if you can simply stub it and also the spec could be a bit cleaner focusing on the actual operation under testing.

Hope it makes sense

fernandes commented 5 years ago

it's so rspec hehehe I prefer the purity (and rubity) of minitest heheh but it's good to learn your technique, can be useful try to make something on minitest, thanks for sharing

brindu commented 5 years ago

@emaglio do you have a way to create a "usable" result object ? As you said this is a problem šŸ˜… When I try something naive it doesn't work (without surprise...) :

describe 'Nested activity' do
  class A < Trailblazer::Operation
    step ->(ctx, **) { p 'nested test' }
  end

  class B < Trailblazer::Operation
    step ->(ctx, **) { p 'test' }
    step Nested(A)
  end
                                                                                                                                                                                                              it 'can be stubbed' do
    end_event = Trailblazer::Operation::Railway::End::Success.new(semantic: :success)
    result_object = [end_event, [{}, {}]]
    expect(A).to receive(:call_with_circuit_interface).and_(result_object)

    B.call
  end
end
apotonick commented 5 years ago

You mean "usable" in terms of what to do with the return values on the circuit interface level, @brindu ?

emaglio commented 5 years ago

@apotonick ā€œusableā€ in order to write a test like (Iā€™m with the phone, hard to write code):

Makes sense?

@brindu maybe I have used the wrong word for that, I donā€™t see it a ā€œproblemā€ but it would be good to have the possibility to do it and no sorry I havenā€™t tried to return a result object yet.

Since the complexity of the result objects maybe we should think to expose some higher level API to return a ā€œfakeā€ object, maybe in TRB-test or TRB-story? @apotonick

brindu commented 5 years ago

ā€œusableā€ in order to write a test like (Iā€™m with the phone, hard to write code):

* if the Nested op returns this signal and this ctx Iā€™ll have this result

This is exactly what I want to do ! For example to isolate the tests from a higher level operation/activity which acts like an orchestrator depending on nested operations result.

panSarin commented 5 years ago

Just to be sure, below example won't work in 2.0 , but will work in 2.1 yup ?

Admin::Staff::Operation::Create does not implement: call_with_circuit_interface

when i try to

expect(Admin::Staff::Operation::Create).to(
        receive(:call_with_circuit_interface)
        .and_return(instance_double(Trailblazer::Operation::Result, success?: true))
      )

And that is how my code where i try to call for nested operation looks like:

step Nested(:get_source_operation!, input: ->(options) do
          {
              "params" => options["params"]
          }
        end)

 def get_source_operation!(options, staff:, **)
          if staff.present?
            Admin::Staff::Operation::Create
          else
            Admin::Staff::Operation::InvitationSend
          end
        end