 Guides
March 9, 2022

### In 30 seconds...

It can be tricky to carry out input/output testing with random variables, (a common requirement for beginner Python assignments!) but we have two simple solutions for this common dilemma!

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

def main():
again = ‘y’
While again = ‘y’ or again = ‘Y’:
print (‘rolling the dice…’)
print (‘their values are: ‘)
print (random.randint(MIN,MAX))
print (random.randint(MIN,MAX))
again = input (‘Would you like to roll again? (y = yes): ‘)

main()

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:

1. A simple test to check that the program produces two random numbers
2. 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.

### Create substitute ‘random’ module

Seeding is a simple and effective solution in most cases, but occasionally it may be necessary to have more control over the numbers generated by the student’s script. Say for instance we need our students’ scripts to produce the same number multiple times.

For this situation, we can employ a simple hack where we create a python file called ‘random.py’ which will act as a stand-in for the built-in ‘random’ python module. In our python file we can define the two functions ‘randint’ and ‘randrange’ which always return a predefined number ensuring that the output of our students’ programs is always the same.

The file should look something like this:

-!- CODE language-py -!-constant = 4

def randint(x, y):
return constant

def randrange(x, y):
return constant

Now that we have our substitute ‘random’ module, we need to upload it as a fixture in the setup portion of CodeGrade’s AutoTest. To make our lives easier when creating our tests we can then move this file from the \$FIXTURES directory to the \$STUDENT directory where our student’s tests will be run. We can do this easily in the Per-student setup script with the command:

`mv \$FIXTURES/random.py \$STUDENT/`

Next, we can begin setting up our tests. Create an IO Test step and, in the Program to run field, run your students code with `python3 dice.py`. Since we already know that the outcome of the dice simulator will provide two rolls each with the value of 4, we can then enter this into the Expected outcome field.

> Note: We can constrain our students’ submissions using the Hand-in requirements in the General tab of the assignment management menu. There we can specify that students may only submit the file `dice.py` and nothing else.

### Streamlining the workflow by using environment variables

An assignment as simple as the example provided above may not need more than one number to be produced but, In certain cases, it may be desirable to test the outcome of a program when the random numbers produced are different. In that instance, Linux environment variables can be used in combination with the surrogate ‘random’ module so that multiple IO tests can be conducted without needing to upload a different file for each desired output. We should, firstly, adapt our ‘random.py’ script accordingly:

-!- CODE language-py -!-import os
num = int(os.getenv(‘RANDNUM’))
def randint(x, y):
return num

def randrange(x, y):
return num

### Next steps

IO tests are great for short, simple assignments but more complex assignments with multiple processes or functions become harder to test this way. CodeGrade also offers a wide variety of ways to evaluate your students’ python code including unit testing, code structure testing and code quality testing. Check out our Help Center to see how you can assess python code with these methods in CodeGrade. Prefer to watch a video? Catch the full python autograding webinar by Devin, here! ### Samuel Natarajan

Teacher Success Manager
Meet Sam, a music enthusiast! He meticulously ensures CodeGrade clients excel and deeply understand the product, creating an unforgettable experience. He sees CodeGrade's cultural diversity and vibrancy as more than just a workplace. #### New in CodeGrade: Community Library

With the CodeGrade Community Library you can easily import fully automatically graded coding assignments into your own coding course. #### Watch our ChatGPT and Coding Education webinar!

Watch CodeGrade's webinar on using ChatGPT in coding courses to help students work with this new tool and even use it in your grading worklfow yourself!  