2016-09-27 163 views
2

我有這樣如何測試程序不運行它[Ruby測試/單元]

def something(x) 
    x = x * 1 
end 

puts "something" 

一些代碼,我想要測試該代碼

require 'something.rb' 
require 'test/unit' 

class StringTest < Test::Unit::TestCase 
    def test_something 
    assert_equal(1, something(1)) 
    end 
end 

它的工作原理,但我有來自文件的所有指令的輸出(我在測試之前看到「某些東西」) 如何在我的代碼中只測試方法而無需全部運行?

回答

1

當你require一個文件,你從字面上「運行它」。這就是你的測試知道something方法是如何定義的 - 因爲它已經初始化了定義。

你真的在問什麼,我想如何在require這個文件的時候沉默puts命令。有幾種可能的方法 - 這裏有一些建議:

請不要直接使用puts。一個真正的原油,但簡單的方法可能是包裝這些調試消息一個輔助方法 - 如:

# something.rb: 

def debug(message) 
    unless $debug_messages_disabled 
    puts message 
    end 
end 

def something(x) 
    x = x * 1 
end 

debug "something" 


# in your spec (spec_helper.rb?): 
$debug_messages_disabled = true 

然而,這種方法根本不能很好地擴展...

一個更好的辦法可能是use a Logger而不是puts。如果你選擇登錄到一個文件,那麼你的問題已經解決了!而且,如果您堅持登錄到stdout,那麼只需在運行測試時提高日誌級別即可 - 只要您有方便的方式來設置此日誌級別即可。例如:

# something.rb: 
# ... 
MyApplication.logger.debug "something" # NOT `puts` 

# config/environments/development.rb 
config.log_level = :debug 

# config/environments/test.rb 
config.log_level = :warn 

......但是這種方法可能太多的努力來設置這樣的單個文件!

這導致最終的簡單選項隱藏這些puts命令的輸出:Suppress the STDOUT in your tests

# spec_helper.rb 
before do 
    IO.any_instance.stub(:puts) # globally 
    YourClass.any_instance.stub(:puts) # or for just one class 
end 

或爲一個更通用的解決方案,可以阻止所有 STDOUT:

#spec_helper.rb 
RSpec.configure do |config| 
    original_stderr = $stderr 
    original_stdout = $stdout 
    config.before(:all) do 
    # Redirect stderr and stdout 
    $stderr = File.open(File::NULL, "w") 
    $stdout = File.open(File::NULL, "w") 
    end 
    config.after(:all) do 
    $stderr = original_stderr 
    $stdout = original_stdout 
    end 
end 
1

由於您的代碼是現在編寫的,因此沒有簡單的方法來運行something方法,而無需首先要求或加載它所包含的文件,這會導致您的puts命令被執行。

我的主要建議是重構你的Ruby文件。您可以將puts語句移入方法中,以使其不會自動運行。大多數Ruby庫都是這樣編寫的:庫中的文件在加載時不會有任何外部可見的副作用;他們只是定義方法,類和模塊。

如果重構是不是出於某種原因的選項,你可以使用這樣的黑客攻擊,以防止越來越打印輸出,但它可能因爲它缺乏良好的POSIX支持不會工作在Windows上:

require 'fcntl' 

puts "this gets printed" 

# Duplicate the stdout file descriptor and then change the original 
# one to be a black hole. 
stdout_copy_fd = $stdout.fcntl(Fcntl::F_DUPFD) 
$stdout.reopen("/dev/null", "w") 

puts "this is blocked" 
# you can require/load your noisy Ruby scripts here 

# Restore the stdout file descriptor. 
$stdout.reopen IO.new(stdout_copy_fd) 

puts "this gets printed too" 
0

要重申

您既可以通過磕碰puts命令實現這一目標我看到它的問題:「我有一個ruby文件,它定義了方法並運行了其他命令,我怎樣才能在不運行命令的情況下測試這些方法?」

如果你的腳本的命令,完全是爲了輸出 - puts,日誌記錄,調試,stdoutstderr等 - 那麼這裏其他的答案是綽綽有餘了。

但是如果你的命令正在做其他的事情,比如設置默認值或者執行有效的加載時間工作呢?或者,也許您的文件被設計爲作爲獨立腳本運行和/或其他文件需要?

解決方案#1:考慮重構

第一個要問的問題是,是否是有意義的重構你的代碼。是否有任何非方法命令(或一系列命令)可用於包含在其他文件中?您是否對運行非方法命令的單元測試感興趣?如果答案都是「是」,那麼通過將方法中的獨立命令包裝起來可以更好地滿足您的需求。

# other methods 

def run_something 
    # do stuff 
end 

run_something 

一般來說,它是很好的做法,讓您的可執行文件儘可能的小,讓他們從你的類和方法定義分離(小bin,大lib)。擴展上面的例子中,你有這樣的事情:

lib/something.rb

# other methods 

def run_something 
    # do stuff 
end 

bin/something.rb

require_relative '../lib/something.rb' 

run_something 

解決方案2:條件執行

每當一個文件運行或加載/需要,文件中的所有命令立即執行。這些命令是類/方法定義還是獨立命令沒有區別。如果你有當另一個文件(如require 'something.rb')加載文件時直接運行應該執行(如ruby something.rb),但不執行任何獨立的命令,你可以測試這種情況,像這樣:

if __FILE__ == $PROGRAM_NAME 
    run_something 
end 

__FILE__是魔法值,在ruby-doc.org記錄如下:

文件的當前正在執行,包括路徑相對 到該應用程序啓動的目錄(或當前 目錄,如果它一直是名改變)。當前文件在某些​​ 的情況下與正在運行的應用程序的啓動文件 不同,後者在全局變量$ 0中可用。

$0$PROGRAM_NAME都是全局變量,可以互換使用(一個是另一個的別名)。

因此表達式if __FILE__ == $PROGRAM_NAME轉換爲「如果當前文件是啓動文件」。