# Objectives

• Learn how to test random behavior
• Learn how to identify and fix over specifying behavior in your tests

Medium

# Problem Statement

Write a program that generates a random number between 0 and 100 (inclusive). The user must guess this number. Each correct guess (it if was a number) will receive the message 'Guess Higher!' or 'Guess Lower!'.

• The prompt should display: 'Welcome to the Guessing Game'
• When the program is run, it should generate a random number between 0 and 100 inclusive.
• You will display a command line prompt for the user to enter their guess number.
• Quitting is not an option. The user can only end the game by guessing the target number. Be sure that your prompt explains them what they are to do.
• Once you have received a value from the user, you should perform validation. If the user has given you an invalid value (anything other than a number between 1 and 100), display an appropriate error message. If the user has given you a valid value, display a message either telling them that there were correct or should guess higher or lower as described above.
• This process should continue until they guess the correct number.

# Steps

## Step 1

Create `guess_game_spec.rb` with the following contents:

``````require_relative 'guess_game'

describe GuessGame do
it 'generates random number between 1 and 100 inclusive' do
game = GuessGame.new
result = game.random

expect(result).to eq(50)
end
end
``````

## Step 2

Create guess_game.rb with the following contents:

``````class GuessGame
def random
50
end
end
``````

This is the minimal implementation that passes the test, but it does not prove that the numbers generated are random.

## Step 3

Here is a test that is based on the 'The TDD Express : From Fast to Fastest' presentation by Bill DePhillips of Thoughtworks.

``````require_relative 'guess_game'

describe GuessGame do
it 'generates random number between 1 and 100 inclusive' do
game = GuessGame.new
result = game.random

expect(result).to be_a Fixnum
end
end
``````

Run the test, it should pass.

## Step 4

There is a relationship between the doc string and the test. In this case, we can change the doc string to reflect what we are testing like this:

``````require_relative 'guess_game'

describe GuessGame do
it 'returns a number' do
game = GuessGame.new
result = game.random

expect(result).to be_a Fixnum
end
end
``````

I am still unease when I look at the assertion. It seems to be coupled to the datatype. It is subtle to recognize the fact that this is an example of over specification. An easy way to find out if your test is over specifying or not is to ask the question: Does this test focus on behavior? The answer is clearly, no. So, this test must be deleted.

## Step 5

Let's see how we can test for randomness. Here is another test that is based on 'The TDD Express : From Fast to Fastest' presentation by Bill DePhillips of Thoughtworks.

``````describe GuessGame do
it 'is random over many runs' do
lots_of_rolls = 100.times.map { GuessGame.new.random }
expect(lots_of_rolls.uniq.sort).to eq((1..100).to_a)
end
end
``````

Create guess_game.rb with the following contents:

``````class GuessGame
def random
Random.new.rand(1..100)
end
end
``````

The tests will pass. But, this is another example for over specifying in the test. Since he uses only [1,2,3,4,5,6] for rolling a dice and generates 100 random numbers. It probably passes when it checks that the dice rolls numbers 1 to 6.

Complexity increases with the problem size. This is also likely fail intermittently, since I am generating random numbers 100 times. My example probably needs 1000 or more random number generation to check for numbers between 1 and 100. This accidental complexity also decreases the performance of the test suite.

• Why generate 100 random numbers?
• What is the minimum number of random numbers you need to write the test?
• How can we specify that the generated random number is within the expected range of numbers?
• What if we can eliminate the loop altogether so that we don't generate 100 random numbers for each test run?

We always strive for the minimal data set we need for a particular scenario when writing a test. We want the test to be as simple as possible. This makes the tests easy to maintain. We will come up with a better solution in the next step.

## Step 6

Can we use a stub? No, you cannot use stub to resolve the random failures because you will stub yourself out. So, what statement can you make about this code that is true? Can we loosen our assertion and still satisfy the requirement? The `guess_game_spec` below deals with the problem of randomness.

``````require_relative 'guess_game'

describe GuessGame do
it 'generates random number between 1 and 100 inclusive' do
game = GuessGame.new
result = game.random

expect(1..100).to cover(result)
end
end
``````

This spec checks only the range of the generated random number is within the expected range of 1 to 100. This test now passes. This test shows you that you don't have to over specify in your tests.

# Summary

In this lesson you learned about testing randomness and how to identify and fix over specifying behavior. In the subsequent lessons we will gradually build the guessing game and learn other design principles and testing techniques.

# Ace the Technical Interview

• Easily find the gaps in your knowledge
• Get customized lessons based on where you are
• Take consistent action everyday
• Builtin accountability to keep you on track
• You will solve bigger problems over time
• Get the job of your dreams

#### Take the 30 Day Coding Skills Challenge

Gain confidence to attend the interview

No spam ever. Unsubscribe anytime.