At a glance
CuraCourse Code Grading does not run unittest or JUnit. It runs a command in the sandbox and compares stdout to Expected Output.
When you add a hidden grader file, that file should do the checking and print:
PASSwhen a check succeedsFAILEDplus a short reason when it does not (what was called, what it got, what you expected)
Set Expected Output to one PASS line per check. Turn on Show diff of actual output and expected output so learners see the FAILED line. Use Exact match.
Whole programs with no grader file still compare printed output the usual way (Pattern A).
Prerequisites
- A Code Editor activity with a Code Grading rubric item. See Add rubric items (manual, AI, and code grading).
- For hidden testers, use Files on the Code Grading item (not starter Hidden Files). Rubric files merge after the student submission, so a grader with a new filename will not overwrite student code. See Best practices for code grading.
How to set up each example
- Add Code Grading on the Code Editor.
- Set Run command as shown in the example (it must compile/run the grader, not only the starter filename).
- Fill Input only when the student program reads stdin. Put each value on its own line.
- Paste Expected Output: for grader files, one
PASSper check; for whole programs, the program’s printed text. - Optionally Add File for
grader.pyorGrader.java. - Click Run Test with a sample solution before you assign the lesson.
Do not name a rubric file the same as the student file (main.py, Main.java). Same path replaces the student file.
Pattern A — Whole program (print / input)
Use this when students write a complete program. No extra grader file. Run their file and compare what it prints.
Python: Hello World
Run command: python3 main.py
Expected Output:
Hello World
Student main.py:
print("Hello World")
Java: Hello World
Run command: javac Main.java && java Main
Expected Output:
Hello World
Student Main.java:
public class Main {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
Python: input and output
Students prompt for first and last name, then print Hello, Jane Doe!
Run command: python3 main.py
Input (one value per line):
Jane
Doe
Expected Output (prompts are part of stdout in Python):
What is your first name? Jane
What is your last name? Doe
Hello, Jane Doe!
If you only care about the greeting, turn on Ignore Case / Ignore Whitespace, or use Partial match with Hello, Jane Doe! as expected output. Add a second Code Grading item with different names (for example Joe / Mazzone) so they cannot hardcode one sample.
Student main.py:
fname = input("What is your first name? ")
lname = input("What is your last name? ")
print("Hello, " + fname + " " + lname + "!")
Java: Scanner input and output
Run command: javac Main.java && java Main
Input:
Jane
Doe
Expected Output (Java nextLine() does not echo the typed text — match what your starter actually prints):
What is your first name?
What is your last name?
Hello, Jane Doe!
Student Main.java:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("What is your first name?");
String fname = in.nextLine();
System.out.println("What is your last name?");
String lname = in.nextLine();
System.out.println("Hello, " + fname + " " + lname + "!");
}
}
Pattern B — Return values (hidden grader that prints PASS / FAILED)
Students write a function/method and do not need a main. You add a hidden grader that calls it, compares the result, and prints PASS or FAILED. CuraCourse only needs to match a list of PASS lines.
Starter should be only the function stub so importing/compiling it does not wait for input. The grader should print only PASS / FAILED lines — not the raw return values.
Python: add_five
Students write add_five(number) that returns the argument plus 5.
Run command: python3 grader.py
Files → add grader.py:
from main import add_five
def check(label, actual, expected):
if actual == expected:
print('PASS')
else:
print('FAILED: ' + label + ' got ' + str(actual) + ', expected ' + str(expected))
check('add_five(5)', add_five(5), 10)
check('add_five(0)', add_five(0), 5)
check('add_five(6)', add_five(6), 11)
Expected Output:
PASS
PASS
PASS
If a student returns the wrong value, they see something like FAILED: add_five(0) got 0, expected 5 in the diff.
Student main.py:
def add_five(number):
return number + 5
Java: addFive
Students write a class with public static int addFive(int number).
Run command: javac Main.java Grader.java && java Grader
Files → add Grader.java:
public class Grader {
static void check(String label, int actual, int expected) {
if (actual == expected) {
System.out.println("PASS");
} else {
System.out.println("FAILED: " + label + " got " + actual + ", expected " + expected);
}
}
public static void main(String[] args) {
check("addFive(5)", Main.addFive(5), 10);
check("addFive(0)", Main.addFive(0), 5);
check("addFive(6)", Main.addFive(6), 11);
}
}
Expected Output:
PASS
PASS
PASS
Student Main.java:
public class Main {
public static int addFive(int number) {
return number + 5;
}
}
Python: average (five numbers → float)
Calling average(1, 5, 7, 4, 10) should return 5.4. Compare with a small tolerance so float noise does not fail the check.
Run command: python3 grader.py
Files → grader.py:
from main import average
def check(label, actual, expected):
if abs(actual - expected) < 0.001:
print('PASS')
else:
print('FAILED: ' + label + ' got ' + str(actual) + ', expected ' + str(expected))
check('average(1, 5, 7, 4, 10)', average(1, 5, 7, 4, 10), 5.4)
check('average(5, 9, 24, 6, 4)', average(5, 9, 24, 6, 4), 9.6)
Expected Output:
PASS
PASS
Student main.py:
def average(a, b, c, d, e):
return (a + b + c + d + e) / 5
Java: average
Run command: javac Main.java Grader.java && java Grader
Files → Grader.java:
public class Grader {
static void check(String label, double actual, double expected) {
if (Math.abs(actual - expected) < 0.001) {
System.out.println("PASS");
} else {
System.out.println("FAILED: " + label + " got " + actual + ", expected " + expected);
}
}
public static void main(String[] args) {
check("average(1, 5, 7, 4, 10)", Main.average(1, 5, 7, 4, 10), 5.4);
check("average(5, 9, 24, 6, 4)", Main.average(5, 9, 24, 6, 4), 9.6);
}
}
Expected Output:
PASS
PASS
Student Main.java:
public class Main {
public static double average(int a, int b, int c, int d, int e) {
int s = a + b + c + d + e;
return (double) s / 5;
}
}
Python: has_repeat
Return True if a string contains the same letter twice in a row.
Run command: python3 grader.py
Files → grader.py:
from main import has_repeat
def check(label, actual, expected):
if actual == expected:
print('PASS')
else:
print('FAILED: ' + label + ' got ' + str(actual) + ', expected ' + str(expected))
check('has_repeat(mississippi)', has_repeat('mississippi'), True)
check('has_repeat(capsized)', has_repeat('capsized'), False)
check('has_repeat(mazzone)', has_repeat('mazzone'), True)
check('has_repeat(this)', has_repeat('this'), False)
Expected Output:
PASS
PASS
PASS
PASS
Student main.py:
def has_repeat(word):
for i in range(len(word) - 1):
if word[i] == word[i + 1]:
return True
return False
Java: hasRepeat
Run command: javac Main.java Grader.java && java Grader
Files → Grader.java:
public class Grader {
static void check(String label, boolean actual, boolean expected) {
if (actual == expected) {
System.out.println("PASS");
} else {
System.out.println("FAILED: " + label + " got " + actual + ", expected " + expected);
}
}
public static void main(String[] args) {
check("hasRepeat(mississippi)", Main.hasRepeat("mississippi"), true);
check("hasRepeat(capsized)", Main.hasRepeat("capsized"), false);
check("hasRepeat(mazzone)", Main.hasRepeat("mazzone"), true);
check("hasRepeat(this)", Main.hasRepeat("this"), false);
}
}
Expected Output:
PASS
PASS
PASS
PASS
Student Main.java:
public class Main {
public static boolean hasRepeat(String str) {
for (int i = 0; i < str.length() - 1; i++) {
if (str.substring(i, i + 1).equals(str.substring(i + 1, i + 2))) {
return true;
}
}
return false;
}
}
Python: print function (void that prints)
Students write print_hello() that prints Hello World (it returns nothing). Capture that print, then emit PASS or FAILED — otherwise the student text would mix with the grader output.
Run command: python3 grader.py
Files → grader.py:
import io
import sys
from main import print_hello
def check(label, actual, expected):
if actual == expected:
print('PASS')
else:
print('FAILED: ' + label + ' got ' + repr(actual) + ', expected ' + repr(expected))
buf = io.StringIO()
old = sys.stdout
sys.stdout = buf
print_hello()
sys.stdout = old
check('print_hello()', buf.getvalue().strip(), 'Hello World')
Expected Output:
PASS
Student main.py:
def print_hello():
print("Hello World")
Pattern C — Classes (toString and void methods)
Construct the object in a hidden grader, compare toString / __str__, and capture anything void methods print so only PASS / FAILED reach stdout.
Java: Employee
Students create Employee with name, 4-digit id, hourly salary, a constructor, toString() in the form:
John Doe -- 4545 -- $15.67 per hour
and getRaise(double amount) that increases salary and prints Nice you got a $1.0 raise!
Run command: javac Employee.java Grader.java && java Grader
Files → Grader.java:
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
public class Grader {
static void check(String label, String actual, String expected) {
if (actual.equals(expected)) {
System.out.println("PASS");
} else {
System.out.println("FAILED: " + label + " got \"" + actual + "\", expected \"" + expected + "\"");
}
}
public static void main(String[] args) {
Employee e = new Employee("John Doe", 4545, 15.67);
check("toString()", e.toString(), "John Doe -- 4545 -- $15.67 per hour");
PrintStream original = System.out;
ByteArrayOutputStream buf = new ByteArrayOutputStream();
System.setOut(new PrintStream(buf));
e.getRaise(1.0);
System.setOut(original);
check("getRaise(1.0) message", buf.toString().trim(), "Nice you got a $1.0 raise!");
check("toString() after raise", e.toString(), "John Doe -- 4545 -- $16.67 per hour");
}
}
Expected Output:
PASS
PASS
PASS
Add a second Code Grading item for another employee (for example Bob Bobbington, 5555, 10.5, raise 0.4). Use a different grader filename per item, or one rubric item per test, so Files do not collide.
Sample student Employee.java:
public class Employee {
private String name;
private int id;
private double salary;
public Employee(String n, int i, double s) {
name = n;
id = i;
salary = s;
}
public String toString() {
return name + " -- " + id + " -- $" + salary + " per hour";
}
public void getRaise(double amount) {
salary += amount;
System.out.println("Nice you got a $" + amount + " raise!");
}
}
Gotcha: 1.0 vs 1 in the raise message depends on how students print amount. If that is not part of the spec, skip the message check and only compare toString() after the raise.
Python: same Employee idea
Run command: python3 grader.py
Files → grader.py:
import io
import sys
from main import Employee
def check(label, actual, expected):
if actual == expected:
print('PASS')
else:
print('FAILED: ' + label + ' got ' + repr(actual) + ', expected ' + repr(expected))
e = Employee('John Doe', 4545, 15.67)
check('str(employee)', str(e), 'John Doe -- 4545 -- $15.67 per hour')
buf = io.StringIO()
old = sys.stdout
sys.stdout = buf
e.get_raise(1.0)
sys.stdout = old
check('get_raise(1.0) message', buf.getvalue().strip(), 'Nice you got a $1.0 raise!')
check('str(employee) after raise', str(e), 'John Doe -- 4545 -- $16.67 per hour')
Expected Output:
PASS
PASS
PASS
Student main.py:
class Employee:
def __init__(self, name, id, salary):
self.name = name
self.id = id
self.salary = salary
def __str__(self):
return f"{self.name} -- {self.id} -- ${self.salary} per hour"
def get_raise(self, amount):
self.salary += amount
print(f"Nice you got a ${amount} raise!")
Partial credit
Each Code Grading item is all-or-nothing. Several PASS lines in one grader still succeed or fail together. To give “2 of 3 tests,” add three items (for example 1 point each), each with its own grader calls or stdin pair.