Skip to content

Create

Create an assignment

Let's create an assignment! You will use an assignment to automatically grade your student's submissions and provide them with instantaneous feedback so that they can improve.

Requirements - Before you begin, you will need an actual programming assignment using a coding language (Java or Python). You will then create your assignment question(s) and the solution to the assignment. You will also create the programmatic unit tests which will be used to test your student's submissions. If you need some inspiration, please reference our examples, where we have shared several assignments used in our courses.

Java unit tests must be in a Gradle project

Java unit tests must be in a Gradle project format prior to uploading the files. For more information, review this article. Your students may submit their files in any format that they'd like.

Make sure you use absolute imports in python projects

For your python unit test code, make sure you use absolute imports instead of relative imports. This will help the auto grader to reliably refactor your student's code to conform to your project's structure.

from package1 import module1
from package1.module2 import function1
from package2 import class1
from package2.sub_package1.module5 import function2
from .module1 import class1
from ..package1 import function1
from . import class2

Let's begin:

1) In the activity bar, on the left side of the screen, click the Create Create Icon Create Icon button.

2) In the menu that appears, click Create an Assignment.

3) In the window that appears, start by typing in the assignment's name in the text field labeled Assignment Name. This name will be displayed to your students.

4) Drag and drop the root directory of your assignment project (which includes your unit test and the problem set solutions) in the box outlined with a blue dashed line (below the Assignment Name text field). You may also click on the file icon File Icon Create Icon (in the middle of the dashed box) to open the file explorer in order to select a file.

Your files have begun the validation process

After inputting your files, the system will automatically begin the project validation process. The validation process may take several minutes. Throughout the process you can set up the other necessary components of your assignment such as the description and due date.

5) Next, you will be taken to a new assignment window known as the Assignment Details view. Learn more about the Assignment Details view.

  • In the Title field, feel free to modify the title of your assignment.

  • In the Visibility field, make sure you move the blue slider to 'true' when you want your students to be able to access the assignment.

  • Copy and paste your assignment description in the Description field.

6) In the blue view tabs bar at the top of the screen, click the tab named Runtime.

7) On the Runtime page, click the field named Student Files in the middle of the screen. In the drop down menu that appears, select the file(s) that you expect your students to submit. Learn more about student files.

8) You are done! In order for your students to make submissions, you must create a course and link the assignment to the course.

Review the Runtime settings before you finish

It's recommended that you review the Runtime settings before you finalize your assignment. These settings will allow you to fine-tune how the code is run.

Writing effective academic unit tests

In this section, we will discuss how to write student friendly unit tests that will adhere seamlessly to an academic setting. Writing an effective unit test for grading assignments is different compared to testing that occurs in a business-like environment. This is because you have to account for the many challenges that are normally taken for granted. One example being, when the function or class is misspelled or does not exist. Along with that, it is important to know how you can properly communicate what happened leading up to an error. Writing great tests for your students requires a few key components:

Describe Input/Output

As professors, we must outline the actions or events that lead up to the failure of a code and the results of that failure. Telling our students the inputs that were used is beneficial because they will be able to use this information to test their code locally. Also, by sharing with them the output, they will be able to learn if their changes have a positive impact locally.

Example input and output messaging:

CLI

In the example below, the student is asked to create a CLI based application the calculates the areas of shapes. If the user typed in a 0 dimension for any of the sides, the application should print an error message.

Notice that we share with the student the steps it takes to recreate the error so that they have the knowledge they will need in order to improve their code and resubmit.

I typed in "triangle" as a shape name. 
Then I typed in "5". 
Then I typed in "0". 
Your application was expected to print: "Error: Zero dimension not allowed". 
Instead it printed the following text: 
"Input a shape: 
What is the height of your triangle: 
What is the base of your triangle: 
The area of the triangle is 0.0"
Functions

In the assignment outline below, the student is asked to create a function named case_count that counts the number of every nth upper or lower case character in a string.

In this specific test below, the function is called with the arguments that instruct the function to count every 7th character that is upper case.

Notice since it's a single function call, we can show them the complete functional call.

Your case count function was called with the following arguments:
case_count("S7E4yCvr9BUxUVlxHLPDX", 7, True)
Your function was expected to return: 1
But your function returned: 2
OOP

In the assignment outline below, the student is asked to create an object that mimics the traditional Java ArrayList class.

Notice we share with the student the steps it takes to recreate the error so that they have the knowledge they need in order to improve their code and resubmit.

Two lists were made, list1 and list2.
First, list1.add("ITEM_1") was invoked then list1.add("ITEM_2") was invoked. 
Then list2.add("ITEM_2") was called. 
Then list1.retainAll(list2) was invoked. 
Finally list1.contains("ITEM_1") was called, false was expected to be returned but it returned true.

Randomize your inputs

When you can, it's best to randomize your inputs, or else students will build their code to pass the unit test rather than actually solving the problem.

Example: If you ask your students to create a function that adds two numbers together and return the sum, but you only test with the inputs 1 and 2, then you may encounter a student that simply writes return 3 in their function. You can't blame them, technically they're right 😉.

Wrap students' code in a try-catch block

As instructors, we cannot account for all the possible issues that may arise when testing our student's code, thus, its often best to wrap any calls to their code in try-catch blocks. When an issue does occur, we have the ability to cleanly printout the steps that lead up to the error. In the event that we don't catch their exception, the student will be presented with full stack traces of the exception, which may not be helpful depending on their expertise.

In the examples below, we show how to handle exceptions in our student's code:

import unittest
from random import randint

from student_homework import add

class ExampleTest(unittest.TestCase):

    def test_add(self):
        x = randint(0, 10)
        y = randint(0, 10)
        expected = x + y
        try:
            result = add(x, y)
        except Exception as e:
            self.fail(f"The function add({x}, {y}) was called but an {e.__name__} occurred because {e}.")
        else:
            fail_msg = f"The function add({x}, {y}) was called and it was expected to return {expected} but it returned {result}."
            self.assertEqual(expected, result, fail_msg)
import org.junit.Assert;
import org.junit.Test;

import java.util.Random;

public class ExampleTest {

    @Test(timeout = 3000) // (1)!
    public void addTest(){
        Random r = new Random();
        int x = r.nextInt(10);
        int y = r.nextInt(10);
        int expected = x + y;
        int result = 0;
        try{
            result = StudentHomework.add(x, y);
        }catch (Exception e){
            Assert.fail("The function add("+x+", "+y+") was called but an "+e.getClass().getName()+" occurred.");
        }
        String failMessage = "The function add("+x+", "+y+") was called and it was expected to return "+expected+" but it returned "+result+".";
        Assert.assertEquals(failMessage, expected, result);
    }
}
  1. It's always good practice to set a time limit on your student's functions

Handling Misspellings or Missing Members/Attributes

Misspellings of names is a common issue particularly when it comes to naming files or internal code attributes such as functions and classes. Fortunately, Grader Than's auto grading system automatically validates file names before the code is run in order to avoid compilation errors (see the Test File section for more details). The auto grader will also be able to handle missing attributes (classes, functions and variables) for python assignments too.

However, when function and classes are missing or misspelled, we end up with a compilation error for a Java project or auto grader error with python applications. This means none of the tests will be run, and the student will not receive a grade for the submission. We have to handle them at runtime. In this section, we will discuss how to handle this issue by displaying an informative error message to our students; in the event your test code cannot find the proper class or function in the student's code.

As you may well know, the solution for this issue differs from Python to Java. Due to the structure of Java, resolving this issue is significantly more complex. We will be releasing a java library shortly that will help you handle this issue.

In the example below we handle the situation when a function named add() is missing:

import unittest
from random import randint

class ExampleTest(unittest.TestCase):

    def test_add(self):
        try:
            from homework import add # (1)!
        except ImportError:
            self.fail("The homework.py module is does not have a function named add().")
        else:
            x = randint(0, 10)
            y = randint(0, 10)
            expected = x + y
            try:
                result = add(x, y)
            except Exception as e:
                self.fail(f"The function add({x}, {y}) was called but an {e.__name__} occurred because {e}.")
            else:
                fail_msg = f"The function add({x}, {y}) was called and it was expected to return {expected} but it returned {result}."
                self.assertEqual(expected, result, fail_msg)
  1. We don't need to check if homework.py exists because that's done for us before running the test by the auto grader system.
// Coming soon