我有一些像这样的代码
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
它有效,但我从文件中的所有指令输出(我看到&#34;某些东西&#34;在测试之前) 如何在我的代码中只测试方法而不运行所有方法?
答案 0 :(得分:1)
当您的代码现在编写时,没有简单的方法来运行something
方法而不首先要求或加载它包含的文件,这会导致您的puts
命令被执行。 / p>
我的主要建议是重构你拥有的Ruby文件。您可以将puts
语句移动到方法中,以便它不会自动运行。大多数Ruby库都是这样写的:库中的文件在加载时不会有任何外部可见的副作用;他们只是定义方法,类和模块。
如果由于某种原因重构不是一个选项,你可以使用这样的hack来防止输出打印,但它可能无法在Windows上运行,因为它缺乏良好的POSIX支持:
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"
答案 1 :(得分:1)
当你require
一个文件时,你实际上 “正在运行它”。这就是您的测试知道something
方法已定义的方式 - 因为它已经初始化了定义。
我想,你真正要问的是如何在puts
文件时使require
命令静音。有几种可能的方法 - 这里有一些建议:
请勿直接使用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。
您可以通过仅存根puts
命令来实现此目的:
# 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
答案 2 :(得分:1)
在我看来时重述问题:“我有一个定义方法并运行其他命令的ruby文件。如何在不运行命令的情况下测试方法?”
如果您的脚本命令仅用于输出 - puts
,日志记录,调试,stdout
或stderr
等 - 那么此处的其他答案绰绰有余。
但是,如果您的命令正在执行其他设置,例如设置默认值或执行有效的加载时工作,该怎么办?或者您的文件可能是作为独立脚本运行和/或其他文件所需的?
解决方案#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:有条件执行
每当运行或加载/需要文件时,文件中的所有命令都会立即执行。这些命令是类/方法定义还是独立命令没有区别。如果你有文件直接运行时应该执行的任何独立命令(例如ruby something.rb
)但是在被另一个文件(例如require 'something.rb'
)加载时不执行,你可以像这样测试这种情况:
if __FILE__ == $PROGRAM_NAME
run_something
end
__FILE__
是一个神奇的值,在ruby-doc.org记录如下:
当前正在执行的文件的名称,包括路径相对 到应用程序启动的目录(或当前 目录,如果它已被更改)。在某些情况下,当前文件 案例,与正在运行的应用程序的启动文件不同, 它在全局变量$ 0中可用。
$0
和$PROGRAM_NAME
都是全局变量,可以互换使用(一个是另一个的别名)。
因此,表达式if __FILE__ == $PROGRAM_NAME
转换为“如果当前文件是启动文件。”