2013-06-25 54 views
0

我是新來的st /嘲弄。如何殘段或模擬第三方庫

如何從外部庫中存根方法,以便我只能測試我的模塊的方法,而無需實際調用庫?

此外,我想知道,是我寫這個模塊的方法去或違反編程的一些重要原則嗎?

# file_module.rb 
module FileModule 
    require 'net/ftp' 

    @ftp = nil 

    def self.login  
    if [email protected] || @ftp.closed? 
     @ftp = Net::FTP.new(Rails.configuration.nielsen_ftp_server) 
     @ftp.login(Rails.configuration.nielsen_ftp_user, Rails.configuration.nielsen_ftp_password) 
    end 
    end 

    def self.get_list_of_files_in_directory(directory, type) 
    login 
    @ftp.chdir("/#{directory}")   
    files = case type 
     when "all"   then @ftp.nlst("*") 
     when "add"   then @ftp.nlst("*add*") 
    end 
    end 
end 

# file_module_spec.rb (RSpec)  
require 'spec_helper' 
describe NielsenFileModule do 
    describe ".get_list_of_files_in_directory" do 
    it "returns correct files for type all" do 
     # how to mock Net::FTP or stub all its methods so I simulate the return value of @ftp.nlst("*")? 
     NielsenFileModule.get_list_of_files_in_directory("test_folder", "all").count.should eq 6 
    end 
    end 
end 

回答

2

想到這個最簡單的方法是使用Dependency Injection的原則。您可以將任何外部依賴關係傳遞給您正在測試的類。在這種情況下,@ftp對象。

您正在使用類(或靜態)方法在對象上使用成員變量時發生一個錯誤。

考慮修改類來做到以下幾點:

# file_module.rb 
module FileModule 
    require 'net/ftp' 
    attr_accessor :ftp 

    @ftp = Net::FTP.new(Rails.configuration.nielsen_ftp_server) 

    def login  
    if [email protected] || @ftp.closed? 
     @ftp.login(Rails.configuration.nielsen_ftp_user, Rails.configuration.nielsen_ftp_password) 
    end 
    end 

    def get_list_of_files_in_directory(directory, type) 
    login 
    @ftp.chdir("/#{directory}")   
    files = case type 
     when "all"   then @ftp.nlst("*") 
     when "add"   then @ftp.nlst("*add*") 
    end 
    end 
end 
在您的測試

現在,而不是在模塊上測試類的方法,你可以測試模塊上對象的方法。

require 'spec_helper' 
class FileClass 
    include FileModule 
end 

let(:dummy) { FileClass.new } 
let(:net_ftp) { double(Net::FTP) } 
before { dummy.ftp = net_ftp } 

describe FileModule do 
    describe '.login' do 
    context 'when ftp is not closed' do 
     before { net_ftp.stub(:closed) { true } } 
     it 'should log in' do 
     net_ftp.should_receive(:login).once 
     dummy.login 
     end 
    end 
    end 
end 

現在您可以對上面顯示的net_ftp對象進行存根或設置期望。

注意:有很多方法可以做到這一點,但這是一個很好的例子,很有意義。您正在將外部服務解壓縮到可以使用模擬功能進行翻倍和替換的東西。

您也可以存根出類的方法和做這樣一些事情:

Net::FTP.any_instance.stub 

當你更舒服發生了什麼。

+1

@ gmacdokugall的方法是一個很好的方法,但代碼中存在一個可能導致沮喪的隱患:在file_module.rb中的第6行賦予的@ ftp變量不相同@在實例方法中被引用的ftp變量。 '@ ftp'被賦值時的'self'是**模塊**,並且實例方法中的'self'是**實例**,因此兩個不同的'@ ftp'變量被引用。一種解決方案是爲'ftp'提供一個「getter」實例方法,如果它尚未定義,則分配給'@ ftp'。 –

+0

我一直認爲依賴注入只是爲了測試代碼的氣味。 –