2012-12-28 74 views
3

如何在下面的示例中調用Foo.bar而不測試bar方法的行爲(已在別處測試過)?是否可以使用MiniTest測試類方法(例如Foo.bar)?

# Code 
class Alpha 
    def process 
    Foo.bar 
    end 
end 

以下規範是我到目前爲止的內容。不幸的是,由於Foo已經在我的項目的其他地方定義過了,所以這種方法會拋出一個「class already defined」警告。

# Spec 
let(:alpha) { Alpha.new } 
let(:klass) { MiniTest::Mock.new } 

subject { alpha.process } 

it "calls Foo.bar" do 
    klass.expect(:bar, '')  # Define method call expectation 
    Foo = klass    # Redefine Foo as a mock object 
    subject     # Run method being tested 
    klass.verify    # Confirm method was called 
end 

我不希望我的測試依賴於Foo類,因爲這是一個外部的依賴,我不想要測試的Foo.bar響應值,因爲這可能會隨意改變。

回答

6

爲了模擬出一類這樣的,你必須插入一個注射點,像這樣:

class Alpha 
    def initialize(opts = {}) 
    @foo_class = opts[:foo_class] || Foo 
    end 

    def process 
    @foo_class.bar 
    end 
end 

這工作,因爲一個類名僅僅是在Ruby中一個常量,它可以分配像任何其他價值。因此,您無需在Alpha中對Foo類的調用進行硬編碼,而是在您的新實例變量所指向的任何一種情況下調用該方法。大多數情況下,除非你通過別的東西,否則這些東西仍然是Foo類。我通常像這樣將模擬參數隱藏爲可選參數。我也沒有在最終用戶定位文檔中包含對它們的引用,因爲它們在技術上不屬於我認爲是公共API的一部分。

然後在您的測試,就可以初始化你Alpha對象,像這樣:

fooClassMock = MiniTest::Mock.new 
fooClassMock.expect(:bar, '') 
alpha = Alpha.new(:foo_class => fooClassMock) 

,你應該得到你想要的結果。

+0

正確的,確實如此。如果我的「process」方法(按照上面的示例)應該執行@ foo_class.new.bar會怎麼樣?這應該是我的期望? fooClassMock.expect(:bar,'')很簡單,但另一個應該是fooClassMock.expect(:new,Foo)或其他東西?我無法嘲笑這樣的事情。任何提示? –

+0

@ user1955626其實,我相信你應該期待':initialize',而不是期待':new'。 'initialize'的返回值應該是另一個'Mock',它需要':bar'並返回適當的值。但是,無論是期望':new'還是':initialize',關鍵是要返回另一個'Mock',它需要':bar'。 – Lee

+0

關於':initialize'的介紹。關於返回的結果呢?它應該是'fooClassMock.expect(:initialize,the_other_mock.class)'或'fooClasssMock.expect(:initialize,the_other_mock.new)'或類似的東西?顯然已經編寫了像'the_other_moc = MiniTest :: Mock.new'這樣的新模擬和':bar'方法的特定期望。 –

2

我知道這是一個老問題,但我一直在尋找一個解決方案,它不需要修改業務代碼來簡化測試,我想我想出了一個解決方案。

即使你使用Rails,你需要添加gem "minitest"Gemfile,再加入require "minitest/mock"test_helper.rb

it "calls Foo.bar" do 
    bar_is_called = false 
    bar_lambda = ->{ 
    bar_is_called = true 
    } 

    Foo.stub(:bar, bar_lambda) do 
    Alpha.new.process 
    end 
    bar_is_called.must_equal true 
end 

.stub可以傳遞一個返回值,或者通過一些響應.call,它調用就可以了(Docs for the stub method.call。在這個例子中。調用bar_lambda,更改bar_is_called的值。這驗證Foo.bar被調用。

它也同樣在測試語法:

test 'Foo.bar is called' do 
    bar_is_called = false 
    bar_lambda = ->{ 
    bar_is_called = true 
    } 

    Foo.stub(:bar, bar_lambda) do 
    Alpha.new.process 
    end 
    assert bar_is_called 
end 
相關問題