如何测试生成文件的脚本

发布于 2024-10-17 23:47:04 字数 434 浏览 3 评论 0原文

我正在创建一个 Rubygem,它可以让我生成 jekyll post 文件。我开发这个项目的原因之一是学习 TDD。这个 gem 在命令行上严格起作用,它必须进行一系列检查以确保它找到 _posts 目录。这取决于两件事:

  1. 是否传递了 location 选项
    • 该位置选项有效吗?
  2. 未传递位置选项
    • posts 目录是否在当前目录中?
    • posts 目录是当前工作目录吗?

那时,我真的很难测试应用程序的这一部分。所以我有两个问题:

  • 跳过对应用程序的一小部分(如上述部分)的测试是否可以接受/可以?
  • 如果没有,如何使用 minitest 测试 ruby​​ 中的文件操作?

I am creating a Rubygem that will let me generate jekyll post files. One of the reasons I am developing this project is to learn TDD. This gem is strictly functional on the command line, and it has to make a series of checks to make sure that it finds the _posts directory. This depends on two things:

  1. Wether or not a location option was passed
    • Is that location option valid?
  2. A location option was not passed
    • Is the posts dir in the current directory?
    • Is the posts dir the current working directory?

At that point, I am really having a hard time testing that part of the application. So I have two questions:

  • is it acceptable/okay to skip tests for small parts of the application like the one described above?
  • If not, how do you test file manipulation in ruby using minitest?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

¢蛋碎的人ぎ生 2024-10-24 23:47:04

我见过的一些项目将其命令行工具实现为 Command 对象(例如: Rubygems我的换行宝石)。这些对象使用 ARGV 进行初始化,只需调用或执行方法即可启动整个过程。这使得这些项目能够将其命令行应用程序放入虚拟环境中。例如,它们可以将输入和输出流对象保存在命令对象的实例变量中,以使应用程序独立于使用 STDOUT/STDIN。因此,可以测试命令行应用程序的输入/输出。就像我想象的那样,您可以将当前工作目录保存在实例变量中,以使命令行应用程序独立于实际工作目录。然后,您可以为每个测试创建一个临时目录,并将其设置为 Command 对象的工作目录。

现在是一些代码:

require 'pathname'

class MyCommand
  attr_accessor :input, :output, :error, :working_dir

  def initialize(options = {})
    @input = options[:input] ? options[:input] : STDIN
    @output = options[:output] ? options[:output] : STDOUT
    @error = options[:error] ? options[:error] : STDERR
    @working_dir = options[:working_dir] ? Pathname.new(options[:working_dir]) : Pathname.pwd
  end

  # Override the puts method to use the specified output stream
  def puts(output = nil)
    @output.puts(output)
  end

  def execute(arguments = ARGV)
    # Change to the given working directory
    Dir.chdir(working_dir) do
      # Analyze the arguments
      if arguments[0] == '--readfile'
        posts_dir = Pathname.new('posts')
        my_file = posts_dir + 'myfile'
        puts my_file.read
      end
    end
  end
end

# Start the command without mockups if the ruby script is called directly
if __FILE__ == $PROGRAM_NAME
  MyCommand.new.execute
end

现在在测试的设置和拆卸方法中,您可以执行以下操作:(

require 'pathname'
require 'tmpdir'
require 'stringio'

def setup
  @working_dir = Pathname.new(Dir.mktmpdir('mycommand'))
  @output = StringIO.new
  @error = StringIO.new

  @command = MyCommand.new(:working_dir => @working_dir, :output => @output, :error => @error)
end

def test_some_stuff
  @command.execute(['--readfile'])

  # ...
end

def teardown
  @working_dir.rmtree
end

在示例中,我使用 Pathname,这是来自 Ruby 标准库和 StringIO 的一个非常好的面向对象文件系统 API,对于模拟非常有用STDOUT,因为它是一个 IO 对象,流入一个简单的字符串)

在实际测试中,您现在可以使用 @working_dir 变量来测试文件是否存在或内容:

path = @working_dir + 'posts' + 'myfile'
path.exist?
path.file?
path.directory?
path.read == "abc\n"

Some projects I've seen implement their command line tools as Command objects (for example: Rubygems and my linebreak gem). These objects are initialized with the ARGV simply have a call or execute method which then starts the whole process. This enables these projects to put their command line applications into a virtual environment. They could, for example hold the input and output stream objects in instance variables of the command object to make the application independant of using STDOUT/STDIN. And thus, making it possible to test the input/output of the command line application. In the same way I imagine, you could hold your current working directory in an instance variable to make your command line application independent of your real working directory. You could then create a temporary directory for each test and set this one as the working directory for your Command object.

And now some code:

require 'pathname'

class MyCommand
  attr_accessor :input, :output, :error, :working_dir

  def initialize(options = {})
    @input = options[:input] ? options[:input] : STDIN
    @output = options[:output] ? options[:output] : STDOUT
    @error = options[:error] ? options[:error] : STDERR
    @working_dir = options[:working_dir] ? Pathname.new(options[:working_dir]) : Pathname.pwd
  end

  # Override the puts method to use the specified output stream
  def puts(output = nil)
    @output.puts(output)
  end

  def execute(arguments = ARGV)
    # Change to the given working directory
    Dir.chdir(working_dir) do
      # Analyze the arguments
      if arguments[0] == '--readfile'
        posts_dir = Pathname.new('posts')
        my_file = posts_dir + 'myfile'
        puts my_file.read
      end
    end
  end
end

# Start the command without mockups if the ruby script is called directly
if __FILE__ == $PROGRAM_NAME
  MyCommand.new.execute
end

Now in your test's setup and teardown methods you could do:

require 'pathname'
require 'tmpdir'
require 'stringio'

def setup
  @working_dir = Pathname.new(Dir.mktmpdir('mycommand'))
  @output = StringIO.new
  @error = StringIO.new

  @command = MyCommand.new(:working_dir => @working_dir, :output => @output, :error => @error)
end

def test_some_stuff
  @command.execute(['--readfile'])

  # ...
end

def teardown
  @working_dir.rmtree
end

(In the example I'm using Pathname, which is a really nice object oriented file system API from Ruby's standard library and StringIO, which is useful for for mocking STDOUT as it's an IO object which streams into a simple String)

In the acutal test you could now use the @working_dir variable to test for existence or content of files:

path = @working_dir + 'posts' + 'myfile'
path.exist?
path.file?
path.directory?
path.read == "abc\n"
梦情居士 2024-10-24 23:47:04

根据我的经验(因此这是非常主观的),我认为有时在某些难以测试的领域跳过单元测试是可以的。您需要弄清楚您会得到什么回报以及是否进行测试的成本。我的经验法则是,不测试某个类的决定应该是非常不寻常的(大约 300 个类中不到 1 个)

如果您尝试测试的内容非常困难,因为与文件系统的依赖性,我认为您可以尝试提取与文件系统交互的所有位。

From my experience (and thus this is VERY subjective), I think it's ok sometimes to skip unit testing in some areas which are difficult to test. You need to find out what you get in return and the cost for testing or not. My rule of thumb is that the decision to not test a class should be very unusual (around less than 1 in 300 classes)

If what you're trying to test is very difficult, because of the dependencies with the file system, I think you could try to extract all the bits that interact with the file system.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文