Runestone exercises - adding test cases for code with input statements

11 views
Skip to first unread message

Suzanne Matthews

unread,
Mar 17, 2026, 3:56:18 PM (12 days ago) Mar 17
to PreTeXt support
Good morning,

Is it possible to add test cases to Runestone exercises that use the input statement? In the sample textbooks that use runestone exercises, none of the Python problems with test cases use the input statement. 

Can you please demonstrate how to do this in Python? We have been struggling to figure out how to implement this. Our latest attempt looks like the following:

<activity xml:id="forloop-1" >
<title>Exercise (algorithm and implementation)</title>
<statement>
<p> Come up with an algorithm for a program that calculates
                the average grade for three students.
                The program should prompt the instructor to enter a
                grade, then another, the one more, in a loop.
                The program should then output the
                average grade for the section.
</p>
<p>
                Sample Input/Output
</p>
<program language="python">
How many grades: 3
Enter grade: 95.0
Enter grade: 85.0
Enter grade: 90.0
Class Average: 90.0
</program>
</statement>
<program label="10-6-1x" interactive="activecode" language="python">
<code>
def main():

 

    # This part handles the interaction

 

    count = int(input("How many grades: "))

 

    grades = []

 

    for i in range(count):

 

        grades.append(int(input("Enter grade: ")))

 

    avg = sum(grades)/len(grades)

 

    print(f"Average is: {avg}")
main()
====
from unittest.gui import TestCaseGui
from unittest.mock import patch
from io import StringIO
import unittest

class TestGrader(TestCaseGui):

    @patch('builtins.input')
    @patch('sys.stdout', new_callable=StringIO)
    def test1(self, mock_stdout, mock_input):
        """
        Tests main() using mocking.
        Inputs: 2 grades, 10 and 20.
        Expected Output: Average is: 15.0
        """
        # Define the sequence of inputs
        # 1st: count (2), 2nd: grade (10), 3rd: grade (20)
        mock_input.side_effect = ['2', '10', '20']

        # Call the function
        main()

        # Get the printed output and strip whitespace/newlines
        output = mock_stdout.getvalue().strip()

        # Verification
        self.assertEqual(output, "Average is: 15.0")
</code>
</program>
<solution>
<program language="python">
<code>
def main():

 

    # This part handles the interaction

 

    count = int(input("Enter number of grades: "))

 

    grades = []

 

    for i in range(count):

 

        grades.append(int(input("Enter grade: ")))

 

    avg = sum(grades)/len(grades)

 

    print(f"Average is: {avg}")

main()
</code>
</program>
</solution>
</activity>


When we compile and run this, we get the following output:
"An error occurred at the end of your code" 

See the attached image.


Can you please point us in the right direction? We ran this using regular Python unittests locally on our machine, and we believe the unittests should work. Thank you!

-Suzanne


image (1).png

Charilaos Skiadas

unread,
Mar 17, 2026, 4:57:01 PM (12 days ago) Mar 17
to pretext...@googlegroups.com
Hi Suzanne, 

Hopefully what I’ve been doing is mostly the following two patterns:

1. Use the new `iotest` structures. You can see an example here: 

https://github.com/skiadas/python-cs1-exercises-book/blob/c04ab3b2ea3368b1ac21274a1a18a6872941245a/source/sec-2-HW2.ptx#L137

This works if all you want to do is test what a main program prints in response to given input. You simply provide it a code block (could have preamble and postamble), and it will run each iotest case on that code: Feed the input tag into the input stream, run the code, compare the output with the output tag’s block.

2. Used if the user code is a function rather than a standalone program. Use a “custom” input() (I wish there was a better way, maybe there is, but on limited time to reply now so hopefully someone can provide an improvement. An example here:


Key ideas: 

- In the preamble override the default “input()” to make it just read from sys.stdin. The default input() in runestone (which is using https://github.com/skulpt/skulpt ) does some weirder stuff, to provide the “get an alert popup for the user to type in an input text” functionality.
- In the tests simply make a StringIO with the desired input, place it at sys.stdin, run the user code, reset stdin to the normal stream.

Definitely a bit hacky.



I don’t know about the @patch parts and whether skulpt supports those. Hope this helps some though.

Charilaos Skiadas
Department of Mathematics
Hanover College


--
You received this message because you are subscribed to the Google Groups "PreTeXt support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pretext-suppo...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/pretext-support/91177c71-a464-4046-a33a-9c76703f0551n%40googlegroups.com.
<image (1).png>

Reply all
Reply to author
Forward
0 new messages