One of the most common practices in beginner Python courses is to ask students to generate pseudo-random integers. This task is used to teach students a number of important aspects of the Python language such as importing external modules, calling functions and working with stored variables. However, using random variables can make testing more challenging since the output will be different every time we run a student’s program.
In this guide I will demonstrate two simple methods for overcoming the issue of input/output testing with random variables in CodeGrade. I will also explain how environment variables can be used to further streamline this solution.
Setting up an example assignment
First of all, we need to design an assignment which requires our students to generate a random integer. The assignment I have created asks students to make a simple dice roll simulator. All that is required is that they:
- Import random (this can be in the form of “from random import …” with either randint or randrange).
- Generate two random integers in the range from 1-6.
- Print out “You rolled a <number>” for each die.
- Ask the user whether they would like to roll again.
- Terminate the program when the user does not want to roll again.
Following these requirements we end up with the following code:
-!- CODE language-py -!-import random
# Constants for the minimum and maximum values of the random variables
MIN = 1
MAX = 6
again = ‘y’
While again = ‘y’ or again = ‘Y’:
print (‘rolling the dice…’)
print (‘their values are: ‘)
again = input (‘Would you like to roll again? (y = yes): ‘)
A simple assignment like this is best tested with an Input/Output test in CodeGrade. However, because of the random numbers generated in the program, the output is unpredictable and we cannot simply expect a certain output. Therefore we need a work-around to ensure the outputs of our students’ programs are always consistent between submissions.
Seeding random numbers
One such work-around is to use the seed function in Python’s built-in random module. The seed function allows you to generate a consistent set of pseudo-random numbers. As long as the seed string and the commands used to generate the numbers after seeding are always the same, the numbers generated will also always be the same. The set of numbers generated will also be the same across all devices so you can expect the output you generate on your local machine to be the same as the output generated in CodeGrade.
We can apply this to our CodeGrade assignment by running the following command in the Run program field of an IO Test step in AutoTest:
`python3 -ic ‘import random; random.seed(<seed string>); import dice.py; dice.main()’`
Because we know, for instance, that the seed string ‘100’ will produce the same sequence of numbers when `random.randint(1,6)` is called, we can then write this sequence in the expected output. The first four numbers produced with the seed string ‘100’ are 2, 4, 4 and 2.
In our case, we can make two tests:
- A simple test to check that the program produces two random numbers
- A more complex test to see if we can make multiple rolls.
We expect our student’s program to automatically roll the dice when the program is run (without providing any input). Therefore, in our first test we don’t need to enter any inputs and we just enter the first two numbers of the seed sequence in the expected output field. For our second test, we need to tell the program that we want to roll again. Therefore, we enter `y` in the input field and in the expected output field we can enter the first 4 numbers in the seed sequence.
> Note: Because we added the -i flag in our Program to the run command, we can interact with the Input field as we would with the Python interpreter.