trailblazer icon indicating copy to clipboard operation
trailblazer copied to clipboard

how to stub #success?

Open saiqulhaq opened this issue 6 years ago • 8 comments

Hi

sorry this is not an issue my question is, how to stub an operation result

let say I have opA and opB

there is a step in opB that depends on opA, and I want to mock failing operation for opA however because

operation = opA.call

creates anonymous instance class, I can't stub operation.success? result

thanks in advance

saiqulhaq avatar Apr 02 '18 07:04 saiqulhaq

Come over to the gitter channel - you can get more responses there.

Having said that, you just need to call your operation with the params, that would cause it to fail. It could be even an operation that has one step, that returns false for example.

opA.call should be executing operation however, it's hard to tell what's going on without seeing the rest of your code.

konung avatar Apr 02 '18 14:04 konung

I believe you have something like

class A < Trailblazer::Operation
  step Nested( B )

and you want to make B return a failure result? I am neither a mocking expert nor a supporter of this flawed testing approach, since it always creates a wrong feeling of security. Can't you invoke the test in a way the inner op fails? There are ways, though, how to do that with mocking, e.g. you could mock the result from B.call?

apotonick avatar Apr 02 '18 14:04 apotonick

said @apotonick , @saiqulhaq I don't do this kind of approach, but if really need to this, here is the way to go:

require 'test_helper'

class StubTest < ActiveSupport::TestCase
  class OperationMock < OpenStruct
    def success?
      success
    end

    def failure?
      !success
    end
  end

  class OpA < Trailblazer::Operation
    step :run_opa!

    def run_opa!(opts, params:, **)
      params[:force_return]
    end
  end

  class OpB < Trailblazer::Operation
    step :check_opa!

    def check_opa!(opts, params:, **)
      res = OpA.(params)
      res.success?
    end
  end

  test "stub operation failure" do
    OpA.stub(:call, OperationMock.new(success: false)) do
      res = OpB.(force_return: true)
      refute res.success?
    end
  end

  test "stub operation success" do
    OpA.stub(:call, OperationMock.new(success: true)) do
      res = OpB.(force_return: false)
      assert res.success?
    end
  end
end

If you need something else from the operation, you can write the method on OperationMock to keep the interface similar

ps: you know what I do? If I connect to some external service on OpA I stub this connection, not the whole operation, so in my test, I guarantee that both operations are working correctly together, just an idea 😉

fernandes avatar Apr 02 '18 14:04 fernandes

thanks @konung I will go to Gitter channel instead later

@apotonick yes I can invoke test in a way the inner operation fails if the case is same as your example code

class A < Trailblazer::Operation
  step Nested( B )

but i have different case here, sorry I forget which one, I have been working on another feature now, so much backlog :) Thanks for your suggestion

@fernandes awesome, thanks for sharing your code

ps: you know what I do? If I connect to some external service on OpA I stub this connection, not the whole operation, so in my test, I guarantee that both operations are working correctly together, just an idea 😉

Yes I'm agree with you, it makes our code is easier to test

saiqulhaq avatar Apr 04 '18 03:04 saiqulhaq

I find this an interesting problem and we should at least have a good solution to it. :coffee:

apotonick avatar Apr 04 '18 05:04 apotonick

@apotonick what's in your mind?

fernandes avatar Apr 04 '18 11:04 fernandes

Right now, this: image

apotonick avatar Apr 04 '18 11:04 apotonick

At the moment, it seems the problem is present again. I tried the method described above and also tried substituting the following options:

it 'call to suboperation' do expect(Api::V1::Lib::Operation::WaitlistRequests::UnsuspendWaitlistRequests).to receive(:call).and_return(Trailblazer::Operation::Result.new(true, {})) result end

it 'call to suboperation' do expect(Api::V1::Lib::Operation::WaitlistRequests::UnsuspendWaitlistRequests).to receive(:call).and_return(Trailblazer::Activity::Railway::End::Success.new(semantic: :success)) result end

This leads to the following problem:

image

Could you please tell me what I am doing wrong?

dmytro-korzhov-rg avatar Jun 17 '24 12:06 dmytro-korzhov-rg