Testowanie błędu zewnętrznej biblioteki

Kontroler wygląda tak:

def create
  begin
    @operation = current_user.operations.withdraws.build(operations_params)
    if @operation.valid?
     txid = bc.sendfrom(
	user_#{current_user.id}", 
	current_user.withdrawal_address, 
	@operation.amount_in_stc
      )
      if @operation.save
        redirect_to profile_path, flash: { success: I18n.t('flash.success.withdrawal_amount') } and return
      end		
    end
  rescue RPC::JSON::Client::Error => e
    flash.now[:error] = I18n.t('flash.error.withdraw')
  end
  render 'withdraw'
end

Chodzi mi o przetestowanie tego rescue.
Próbuję tak:

/spec/features/user_operations_spec.rb:

describe "User operations" do
  before { User.any_instance.stub(:create_bitcoin_account).and_return(true) }
  subject { page }

  context "signed in user" do

describe "withdrawal page" do
  before do
    mock_bitcoin
    user = FactoryGirl.create(:user)
    deposit = FactoryGirl.create(:operation, user: user)
    sign_in user
    visit withdraw_path
  end

  describe "withdraw error" do
    before do
      RPC::JSON::Client.stub(:sendfrom).and_raise (RPC::JSON::Client::Error.new({ code: -32603, message: 'error' }))
      fill_in "Amount", with: "0,4"
    end

    it "sends money from account" do
      expect { 
        click_button "Withdraw" 
        page.should have_content "Error sending money from account"
      }.to_not change(Operation, :count)
    end
  end
end
end
end

/spec/support/utilities.rb:

def mock_bitcoin
 bc_mock = double("bc")
 bc_mock.stub(:validateaddress).with(any_args()).and_return({'isvalid' => true})
 bc_mock.stub(:getnewaddress).and_return('1234')
 bc_mock.stub(:setaccount).and_return(true)
 bc_mock.stub(:move).with(any_args()).and_return(true)
 bc_mock.stub(:sendfrom).with(any_args()).and_return(true)
 RPC::JSON::Client.stub!(:new).and_return(bc_mock)
end

I ten test nie przechodzi. Z wyświetlonej treści strony wynika, że tworzy i poprawnie zapisuje operację, ten stub w ogóle nie ma wpływu.
Mam kilka innych testów, gdzie mockowanie RPC_JSON działa dobrze a tutaj coś robię źle, tylko co??

RPC::JSON::Client.stub

Nie przypadkiem?

RPC::JSON::Client.any_instance.stub

Sprawdziłem, niestety to nie pomogło.

Bardzo duzo zlego sie dzieje w tym kodzie:

  1. mockujesz rzeczy w acceptance spec czego nie powinno sie robic
  2. mockujesz interfejsy, ktore nie sa Twoje czego nie powinno sie robic (big mocking no-no)
  3. brakuje tutaj enkapsulacji dla BC i RPC::JSON (co wynika z 1 & 2) gdybys ja mial to moglbys mockowac w swoich unit testach wlasne interfejsy a pozniej w acceptance specs moglbys uzyc vcr do nagrania remote call i tyle

Dzięki. Pewnie dlatego nie mogłem nic znaleźć konkretnego na ten temat.
Trzeba będzie przemyśleć cały design i zrefaktoryzować, bo na razie z każdym testem jest coraz więcej zabawy z mockowaniem :frowning:

Hmm… trochę to poprawiłem, zrobiłem wrappera do RPC::JSON i jakoś to teraz działa - przynajmniej testuje brak połączenia.

Pytanie czy można tutaj jeszcze coś poprawić (albo inaczej: co tu jest jeszcze nie tak jak powinno być). To jest przypadek, kiedy można utworzyć model (User) niezależnie od tego, czy zew. serwis działa czy nie (mam też drugi przypadek, gdzie działanie zewnętrznego systemu jest konieczne do utworzenia poprawnego modelu).