Passing Index Loop Vars To Objects in Ruby with TDD

83 views Asked by At

I'm trying to get a very simple set of tests done in TDD and Ruby.

The problem I'm having is in trying to pass a range of values from the test to the object being tested.

The aim of the code is to guess the correct 'secret number' by sending the object a range of 'guess' values iteratively via a for-loop, the values being in the range of 1 to 10.

The test should confirm the following...

  1. When the 'guess' value is less than 5 (5 is a fixed value set for the 'secret number), the object should return a symbol of ':less'.

  2. When the 'guess' value is equal to 5 the object should return a symbol of ':found_secret_number'.

  3. When the 'guess' value is greater than 5 the object should return a symbol of ':greater'.

I find that whilst the loop does cycle through and generate the required values, the loop only assigns the final loop value generated to each test (that value being 10). I guess the test creates all of the tests within the loop then assigns whatever value is set at for the lop variable at the end (if you have a quick look at the code it may make more sense...).

The test works with static variables being assigned to the objects, so the class is good from a functional perspective, but I don't want 100% line coverage (e.g. 'guess' values of 3, 5 and 7), but want 100% value coverage (e.g. 1,2,3,4,5,6,7,8,9 and 10).

I've played and tinkered with the code but can't find a way to assign the range of values I'm looking for (1..10), without having to write 10 static cases for the value coverage, so does anyone have a suggestion of how to do this without the use of ten static cases?:)

I'm still learning by the way, so if you could keep any answers as simple as possible it would help;) Likewise, me explaining the problem helps you to understand it, any feedback on how I could have explained the problem better would be really appreciated; comms, as I'm sure you all know, is very important, and I'm trying to improve this too.

Thanks!:)

require 'rspec'

class Game

  def initialize(secret_number)
    @secret_number = secret_number
  end

  def guess(number)
    if (number < @secret_number)
      :lower
    elsif
      (number > @secret_number)
      puts ("number is: " + number.to_s)

      :greater
    elsif (number == @secret_number)
      :found_secret_number
    end
  end
end

# 'describe' is a method, that is passed a 'Game' class,
# it's not normally written like this but I've just shown it this
# way, in this case, to affirm its just plain old Ruby.
describe(Game) do
  subject { Game.new(5) }
  describe '#guess' do
    for i in 1..10
      if (i < 5)
        puts ("i is less than 5, it is: " + i.to_s)
        context 'when guessing a number that is lower than the secret number ' do
          it 'returns the symbol :lower' do
            expect(subject.guess(val)).to eq(:lower)
          end
        end

      elsif (i == 5)
        puts ("i is equal to 5, it is: " + i.to_s)
        context 'when guessing a number that is the SAME as the secret number ' do
          it 'returns the symbol :found_secret_number' do
            expect(subject.guess(val)).to eq(:found_secret_number)
          end
        end

      elsif (i > 5)
        puts ("i is greater than 5, it is: " + i.to_s)
        context 'when guessing a number that is higher than the secret number ' do
          it 'returns the symbol :greater' do
            expect(subject.guess(val)).to eq(:greater)
            end
         end
      end
    end
  end
end
1

There are 1 answers

4
Max On BEST ANSWER

Essentially all of the fancy testing methods like describe, context, it are just ways of defining testing methods. Rspec then calls these testing methods when it actually runs its tests. So it doesn't really make sense to be calling them inside a loop like that, because you're dynamically defining tests (when it definitely isn't needed in your case). Instead you should get rid of the loop, and define all of your expectations within those test cases. For example:

describe(Game) do
  subject { Game.new(5) }
  describe '#guess' do
    context 'when guessing a number that is lower than the secret number ' do
      it 'returns the symbol :lower' do
        (1..4).each { |val| expect(subject.guess(val)).to eq(:lower) }
      end
    end

    context 'when guessing a number that is the SAME as the secret number ' do
      it 'returns the symbol :found_secret_number' do
        expect(subject.guess(5)).to eq(:found_secret_number)
      end
    end

    context 'when guessing a number that is higher than the secret number ' do
      it 'returns the symbol :greater' do
        (6..10).each { |val| expect(subject.guess(val)).to eq(:greater) }
     end
    end
  end
end

Then when I run that file with rspec, I get

...

Finished in 0.02421 seconds (files took 0.26321 seconds to load)
3 examples, 0 failures