I have a very simple Ruby implementation of a game called "FizzBuzz" (i.e. given an input number, it returns "Fizz" if the number is multiple of 3, "Buzz" if multiple of 5, "FizzBuzz" if multiple of both and the original number if it doesn't fit any of the previous condition):
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
I wrote a test using RSpec to validate each one of the conditions:
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
The test works perfectly, however, I'm using concrete values in it (i.e. 3, 5 and 11).
My question is: what if I wanted to test my FizzBuzz Ruby script using a wide range of values (e.g. from 1 to 10000)?
I know I can solve this by using each loops and cases directly in RSpec, however, my concern is that if in my test I adopt the same conditional statements as in the Ruby script to be tested (i.e. when number%3 == 0 then "Fizz"
, etc.) I will end up testing my code using an RSpec script that follows exactly the same logic as the script to be tested, hence the test will probably pass successfully.
What would be the alternative? Are there best practices to write tests using a wide pool of values (e.g. using a loop) rather than hard-coded or specific values?
Running tests by their file or directory names is the most familiar way to run tests with RSpec. RSpec can take a file name or directory name and run the file or the contents of the directory. So you can do: rspec spec/jobs to run the tests found in the jobs directory.
The it Keyword The word it is another RSpec keyword which is used to define an “Example”. An example is basically a test or a test case. Again, like describe and context, it accepts both class name and string arguments and should be used with a block argument, designated with do/end.
RSpec is a testing tool for Ruby, created for behavior-driven development (BDD). It is the most frequently used testing library for Ruby in production applications. Even though it has a very rich and powerful DSL (domain-specific language), at its core it is a simple tool which you can start using rather quickly.
The main difference between RSpec and Cucumber are the business readability factor. Cucumber's main draw is that the specification (features) are separate from the test code, so your product owners can provide or review the specification without having to dig through code.
A possible half-way point here is to loop through possible answers in your RSpec tests. Keeping your code DRY is important but so is keeping your tests DRY and this is sometimes under-estimated.
How about something like this:
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
That way, you can add tests easily by adding them to the expected_values
hash, but if the method name changed or something similar you would only have to change it in one place
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With