Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a test which loops through a range of values using RSpec?

Tags:

tdd

ruby

rspec

bdd

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?

like image 980
Albz Avatar asked Jan 18 '16 14:01

Albz


People also ask

How do I run a specific test in RSpec?

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.

What RSpec method is used to create an example?

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.

Is RSpec TDD or BDD?

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.

What is the difference between RSpec and cucumber?

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.


1 Answers

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

like image 164
Yule Avatar answered Sep 19 '22 12:09

Yule