如何测试程序而不运行它[ruby test / unit]

时间:2016-09-27 16:02:04

标签: 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

它有效,但我从文件中的所有指令输出(我看到&#34;某些东西&#34;在测试之前) 如何在我的代码中只测试方法而不运行所有方法?

3 个答案:

答案 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,日志记录,调试,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:有条件执行

每当运行或加载/需要文件时,文件中的所有命令都会立即执行。这些命令是类/方法定义还是独立命令没有区别。如果你有文件直接运行时应该执行的任何独立命令(例如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转换为“如果当前文件是启动文件。”