0

我想知道这个解决方案是否通过在 ruby​​ 中模拟对象得到社区的认可。如果不是,请描述原因,以及如何改进代码设计或测试。

考虑我有以下代码。

lib/config_loader.rb

class ConfigLoader
  HOME_DIR = '~/my/home_dir'.freeze
  ...

  def load(path)
    path = File.expand_path(path)

    if File.exist?(path)
      File.read(path) 
    else
      File.read(HOME_DIR)
    end
  end
end

在测试时,我实际上不希望在path我在变量中定义的内容下创建任何东西,所以我只想模拟这种行为

spec/lib/config_loader_spec.rb

RSpec.describe ConfigLoader do
  describe '.load' do
    subject { described.class.new.load(path) }

    let(:path) { 'path/that/should/exist' }

    context 'when path' do
      before do 
        allow(File).to receive(:exist?).with(path).and_return(true)
        allow(File).to receive(:read).with(path).and_return('my content')
      end

      it { expect { subject }.to_not raise_error { Errno::ENOENT }
    end
  end
end

也许我应该做一些class_double课程File。我不确定我提供的方式,所以我需要一些信息如何以常见/最佳实践的方式进行

4

1 回答 1

2

测试驱动/行为驱动开发/设计的基本原则之一是不要模拟你不拥有的东西(在Steve Freeman 和 Nat Pryce的测试指导下的《成长面向对象的软件》一书中创造)。

你的例子违反了这个原则:你不拥有File,因此你不应该嘲笑它。

相反,您可以做的是创建自己的抽象来与文件系统交互,它只具有您实际需要的功能。然后你可以创建这个抽象的两个实现:一个使用 Ruby 的File类,一个不做任何事情的模拟。如果你想变得花哨,你甚至可以创建一个模拟内存中的文件系统。

当然,您现在只是解决了问题:您的文件抽象实现中现在有未经测试的代码。但是,理想情况下,此代码应该“几乎”微不足道。显然,您仍然可以对文件抽象实现进行集成测试,也可以对ConfigLoader使用真实实现而不是模拟或模拟的集成测试进行集成测试。

如果您有兴趣,这里有一些进一步的阅读:

于 2020-11-04T09:42:53.557 回答