ruby - 如何测试一个元素是从列表中随机选择的?

标签 ruby unit-testing random rspec tdd

我正在开发 Rails 应用程序并尝试练习 TDD(使用 RSpec)。我的 lib 目录中有一个文件,其中包含一个字符串列表,以及一个读取该文件并从列表中随机选择一个字符串的方法。我还没有实现这个方法,因为我正在为如何编写测试该功能而苦苦挣扎。

有很多方法可以从数组中随机选择一个对象,还有很多很好的回答问题,比如 this one在这里告诉我如何做到这一点(当涉及到实现时,我可能会使用 Array#sample)。但我的期望应该是什么?我在想类似的东西:

期望(数组).to include(subject.random_select)

这肯定会断言我的方法返回了一些预期值——但是否足以断言该方法每次随机返回不同的字符串?有哪些替代方法,或者额外的测试可以确保我已经覆盖了这种方法?我真的不能期望 subject.random_select 等于伪造的输入,可以吗?

最佳答案

我会首先测试从单行文件中非随机选择单个字符串,然后测试从多行文件中选择字符串,最后测试选择是否是随机的。你不能在有限的时间内真正测试随机性,所以你能做的最好的就是

  • 测试您的方法是否返回所需范围内的值,因为您的测试将在应用程序的生命周期内运行很多次,您可能会发现它是否会返回超出范围的值,并且<
  • 证明您的代码使用了随机源。

假设该文件在您的测试环境中不存在,或者您不知道其内容,或者不希望它对一个测试正确而对其他测试不正确的不对称性,那么我们将需要提供一种方法让测试将类指向不同的文件。

我们可以编写以下代码,一次编写一个测试,使其通过并在编写下一个之前进行重构。下面是第三个测试写完后,实现前的测试和代码:

规范/模型/thing_spec.rb

describe Thing do
  describe '.random_select' do
    it "returns a single line from a file with only one line" do
      allow(Thing).to receive(:file) { "spec/models/thing/1" }
      expect(Thing.random_select).to eq("Thing 1")
    end

    it "returns a single line from a file with multiple lines" do
      allow(Thing).to receive(:file) { "spec/models/thing/2" }
      expect(Thing.random_select).to be_in(['Thing 1', 'Thing 2'])
    end

    it "returns different lines at different times" do
      allow(Thing).to receive(:file) { "spec/models/thing/2" }
      srand 0
      thing1 = Thing.random_select
      srand 1
      thing2 = Thing.random_select
      expect(thing1).not_to eq(thing2)
    end

  end
end

应用程序/模型/thing.rb

class Thing
  def self.random_select
    "Thing 1" # this made the first two tests pass, but it'll need to change for all three to pass
  end

  def self.file
    "lib/things"
  end

end

当我编写第二个测试时,我意识到它在没有任何额外代码更改的情况下通过了,所以我考虑删除它。但我推迟了那个决定,编写了第三个测试,并发现一旦第三个测试通过,第二个测试就会有值(value),因为第二个测试测试该值来自文件,但第三个测试没有。

be_in 是一种比 include 更好的测试返回值是否在已知集合中的方法,因为它将实际值放在 expect 中RSpec 期望的位置。

还有其他方法可以控制随机性,因此您可以测试它是否已被使用。例如,如果您使用 sample,您可以 allow_any_instance_of(Array).to receive(:sample) 并返回您喜欢的任何内容。但我喜欢使用 srand,因为它不需要实现来使用使用随机数生成器的特定方法。

如果文件丢失或为空,您也需要对其进行测试。

关于ruby - 如何测试一个元素是从列表中随机选择的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36580186/

相关文章:

ruby - IntelliJ + Ruby 插件与 RubyMine 之间的最大区别是什么?

ruby - 使用 .any 方法迭代数组组合方法

c# - 在单元测试中使用 WPF Dispatcher 的正确方法

java - 掷骰子,有 50% 的几率掷出 6

ruby-on-rails - 找不到有效的 gem : certificate verify failed

java - 如何在 Maven 中添加单元测试的类路径?

unit-testing - 将 TDD 与 Web 应用程序开发集成的最佳实践?

c# - 是否有任何工具可以用随机数据填充类属性?

c# - "good"这个方法是怎么产生随机数的?

ruby-on-rails - 创建具有关联的模型实例