如何编写一个使用RSpec循环遍历一系列值的测试?

Alb*_*lbz 7 ruby tdd bdd rspec

我有一个名为"FizzBu​​zz"的游戏的非常简单的Ruby实现(即,如果数字是3的倍数则返回"Fizz",如果是5的倍数则返回"Buzz",如果是多个,则返回"FizzBu​​zz"和原始数字,如果它不符合以前的任何条件):

class FizzBuzz
    def answer(number)
        multiple3 = number%3 == 0
        multiple5 = number%5 == 0
        return case
        when (multiple3 and multiple5) then "FizzBuzz"
        when multiple3 then "Fizz"
        when multiple5 then "Buzz"
        else number
        end
    end
end
Run Code Online (Sandbox Code Playgroud)

我使用RSpec编写了一个测试来验证每个条件:

require "rspec"
require "./fizzBuzz"

RSpec.describe "#answer" do
    it "returns Buzz when number is multiple of 3" do 
        result = FizzBuzz.new.answer(3)
        expect(result).to eq("Fizz")
    end

    it "returns Buzz when number is multiple of 5" do 
        result = FizzBuzz.new.answer(5)
        expect(result).to eq("Buzz")
    end

    it "returns a number when the input number is neither multiple of 3 nor 5" do 
        result = FizzBuzz.new.answer(11)
        expect(result).to eq(11)
    end

end
Run Code Online (Sandbox Code Playgroud)

测试完美无缺,然而,我正在使用具体值(即3,5和11).

我的问题是:如果我想使用各种值(例如从1到10000)测试我的FizzBu​​zz Ruby脚本怎么办?

我知道我可以通过直接在RSpec中使用每个循环和案例来解决这个问题,但是,我担心的是,如果在我的测试中我采用与要测试的Ruby脚本相同的条件语句(即when number%3 == 0 then "Fizz"等等),我将最终使用RSpec脚本测试我的代码,该脚本遵循与要测试的脚本完全相同的逻辑,因此测试可能会成功通过.

会有什么选择?是否有使用大量值(例如使用循环)而不是硬编码或特定值来编写测试的最佳实践?

Yul*_*ule 19

这里可能的中途点是在RSpec测试中循环可能的答案.保持代码干燥很重要,但保持测试干燥也是如此,这有时会被低估.

这样的事情怎么样:

  RSpec.describe "#answer" do
      expected_values = {'3': 'Fizz', '5': 'Buzz', '6': 'Fizz', '11': '11', '15': 'FizzBuzz'}
      expected_values.each do |val, expected| 
        it "returns #{expected} when number is #{val}" do 
            result = FizzBuzz.new.answer(val.to_i)
            expect(result).to eq(expected)
        end
      end  
    end
Run Code Online (Sandbox Code Playgroud)

这样,您可以通过将测试添加到expected_values哈希中来轻松添加测试,但如果方法名称发生更改或类似,您只需在一个地方更改它


小智 8

为了支持已接受的答案,我建议将更广泛的测试包装在上下文块中,以便测试与其他代码保持隔离:

RSpec.describe "#answer" do

  context 'when testing inputs and answers' do

    RESULTS = { 3 => "Fizz",
                5 => "Buzz",
                11 => 11 }

    RESULTS.each do |value, answer|
      it "returns #{answer} when the input is #{value}" do 
        result = FizzBuzz.new.answer(value)
        expect(result).to eq(answer)
      end
    end

  end

end
Run Code Online (Sandbox Code Playgroud)