Coding Questions
Before you begin writing coding questions, you must have PrairieLearn installed locally (instructions here). The guide for this section will run through the development process of a coding question already implemented, arrayContainsDuplicate
, which you can find by that QID on PrairieLearn, or by that folder name inside pl-virginia-cs2100/questions
. All steps and code used to write this question will be shown on this guide, along with blank templates you can use to create new questions (the overall process is the same for all coding questions). Follow the below steps in order:
-
In IntelliJ, create a blank project you will use to develop a solution to the question and JUnit test cases. You can reuse the same IntelliJ project to write multiple questions (delete the contents of each file and start over).
-
Follow the instructions here (only the “Add dependencies” section) to set up JUnit for this project.
-
If the question will ask the student to simply write a method, create a file in
src
calledSolution.java
. If the question will ask the student to write or fill in missing code of a class, then create a file with the name of the class the student will need to edit to answer the question. Inside this class, write the starter code and your solution to the problem. For the questionarrayContainsDuplicate
, the student’s method takes in anint
array and must returntrue
if the array contains any duplicate values, orfalse
otherwise. Here is an exampleSolution.java
for this question (I know this isn’t the most efficient solution, but most students would come up with this solution):public class Solution { public boolean containsDuplicate(int[] nums) { for (int i = 0; i < nums.length - 1; i++) { for (int j = i + 1; j < nums.length; j++) { if (nums[i] == nums[j]) { return true; } } } return false; } }
If your question requires additional classes (e.g. a linked list question would require a
Node
class), then also add that class to thesrc
folder. The student will only be able to edit one specified class, but there will be an additional step later on in order to ensure the additional classes can be used in the autograder. -
Add a new class called
Tests.java
to thesrc
folder. Paste the following template into this file:import org.junit.jupiter.api.Test; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Tag; import static org.junit.jupiter.api.Assertions.*; public class Tests { }
-
Now, write all the test cases for this question. There should be about 1-3 example test cases that should be shown to the student to ensure they understand the problem. Each test should be written using this template:
@Test @DisplayName("Test name") @Tag("points=_") void testName() { }
The
@DisplayName
tag should contain the test case name, formatted like normal English. If this is an example test case, the display name should start with “Example” (If there is only 1 given example, use “Example”, otherwise, use “Example 1”, “Example 2”, etc.)The tag containing a point value should contain
"points=x"
, wherex
is a number representing the amount of points for this test case.Ensure you create as many tests as necessary to judge the student’s submitted code, including edge cases (may be only a few test cases). Edge cases should not be included in the example test cases, but make sure the edge cases are realistic (e.g. add an assumption that the input will never be
null
). Do not use dynamic randomly generated test cases, as it could cause different results for students with similar submissions.Here is the
Tests.java
file forarrayContainsDuplicate
:import org.junit.jupiter.api.Test; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Tag; import static org.junit.jupiter.api.Assertions.*; public class Tests { @Test @DisplayName("Example 1") @Tag("points=1") void example1() { Solution sol = new Solution(); assertTrue(sol.containsDuplicate(new int[] {1, 2, 1, 3})); } @Test @DisplayName("Example 2") @Tag("points=1") void example2() { Solution sol = new Solution(); assertFalse(sol.containsDuplicate(new int[] {1, 2, 3}) ); } @Test @DisplayName("Empty array") @Tag("points=0.5") void emptyArray() { Solution sol = new Solution(); assertFalse(sol.containsDuplicate(new int[] {})); } @Test @DisplayName("1 element") @Tag("points=0.5") void oneElement() { Solution sol = new Solution(); assertFalse(sol.containsDuplicate(new int[] {1})); } @Test @DisplayName("All same number") @Tag("points=1") void allSameNumber() { Solution sol = new Solution(); assertTrue(sol.containsDuplicate(new int[] {5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5})); } @Test @DisplayName("Large array, no duplicates") @Tag("points=2") void largeArrayNoDuplicates() { Solution sol = new Solution(); assertFalse(sol.containsDuplicate(new int[] {-59, 7, -38, 52, -17, 69, -86, -25, -63, 50, -23, -29, -22, -65, 54, -2, -45, -41, 17, 43})); } @Test @DisplayName("Large array, 1 duplicate") @Tag("points=2") void LargeArrayOneDuplicate() { Solution sol = new Solution(); assertTrue(sol.containsDuplicate(new int[] {-59, 7, -38, 52, -17, 69, -86, -25, -2, -63, 50, -23, -29, -22, -65, 54, -2, -45, -41, 17, 43})); } @Test @DisplayName("Large array, many duplicates") @Tag("points=2") void largeArrayManyDuplicates() { Solution sol = new Solution(); assertTrue(sol.containsDuplicate(new int[] {-4, -3, 4, -4, -5, 2, 4, -3, 2, 5, 2, -1, -2, -1, -5, -3, -2, 4, 0, -3})); } }
-
Start up PrairieLearn locally and go to the CS 2100 “Questions” tab. Click “Add question” on the right. The title for the question should be a short description of the question, formatted in normal English (e.g. Array contains duplicate). Students will see the title of the question while taking the quiz, so it should not give away the solution. The QID for the question should be the same, or similar to the title, but in camelCase (e.g. arrayContainsDuplicate). Students will not be able to see the QID. Leave the “Start from Empty Question” option as it is. Click “Create”.
-
Open the
pl-virginia-cs2100
folder in VS Code.If you are on Windows, open the terminal in VS Code and run the following commands:
cd ~
followed bysudo chown -R [yourusername] .
This will make you the owner of the files, since that is not the default.Navigate to the
questions
folder and then the subfolder with the QID of the question you just created. -
Inside this folder, create a folder called
tests
. Inside thetests
folder, create a folder calledjunit
. Inside thejunit
folder, create a file calledTests.java
. Paste your entireTests.java
file from IntelliJ into the newly createdTests.java
in VS Code. Your folder structure should look like this: -
Open the file
info.json
. Paste the following JSON data right after “v3”:, "gradingMethod": "External", "externalGradingOptions": { "enabled": true, "image": "prairielearn/grader-java", "timeout": 20 }, "tags": ["coding"], "singleVariant": true
You can edit the “timeout” parameter (max seconds the autograder will run for before stopping) if you think your question’s autograder may take more than 20 seconds per submission (20 seconds should be fine for most, if not all, questions)
Your
info.json
file should look something like this now (uuid, title, and topic will be different):{ "uuid": "36290743-9bfa-4e1c-bb42-4f8bad8537ff", "title": "Array contains duplicate", "topic": "Arrays", "type": "v3", "gradingMethod": "External", "externalGradingOptions": { "enabled": true, "image": "prairielearn/grader-java", "timeout": 20 }, "tags": ["coding"], "singleVariant": true }
-
Open the file
server.py
and paste the following code:def generate(data): data["params"]["_grader"] = { "type": "java", "workingDirectory": "tests", "compile": { "command": "javac", "args": ["-cp", ".:junit/*", "../student_code/Solution.java", "junit/Tests.java"] }, "execute": { "command": "java", "args": ["-cp", ".:junit/*", "org.junit.runner.JUnitCore", "Tests",] } }
If the file that the student will edit is not called
Solution.java
, change the name of the file in line 7 to the name of the file the student will edit. -
Open the file
question.html
and paste the following template.<pl-question-panel> <markdown>Question instructions</markdown> <p style="font-weight: bold;">Example 1:</p> <div style="margin-left: 20px;"> <markdown>Input: `param = value`</markdown> <markdown>Output: `output`</markdown> <markdown>Explanation: Why the input produced that output</markdown> </div> <p style="font-weight: bold;">You may assume:</p> <div style="margin-left: 20px;"> <markdown>Assumptions the student can make that won't be tested in hidden test cases</markdown> </div> <java-quick-reference></java-quick-reference> <pl-file-editor file-name="Solution.java" ace-mode="ace/mode/java">public class Solution { public returnType methodName(params) { // Type code here. Do not change the class or method headers. } }</pl-file-editor> </pl-question-panel> <pl-submission-panel> <pl-external-grader-results></pl-external-grader-results> <pl-file-preview></pl-file-preview> </pl-submission-panel>
This html file is what is shown to the students when they are taking the quiz. See the question.html Formatting page for how to use Markdown and properly use this template to format the question.
Include all example test cases in
question.html
and copy that portion of the template multiple times if there are multiple examples. The text inside thepl-file-editor
tag is the starter code for the question. Replace it with your solution for testing, and then delete your solution fromquestion.html
before committing and pushing. If the submitted file is not calledSolution.java
, be sure to edit thefile-name
parameter in thepl-file-editor
tag to match the name of the submitted file.Here is what
question.html
looks like forarrayContainsDuplicate
:<pl-question-panel> <markdown>Develop a method that determines whether an integer array `nums` contains any duplicate values. The method should return `true` if any element is appears more than once, and `false` if every element in the array is distinct.</markdown> <p style="font-weight: bold;">Example 1:</p> <div style="margin-left: 20px;"> <markdown>Input: `nums = {1, 2, 1, 3}`</markdown> <markdown>Output: `true`</markdown> <markdown>Explanation: The element 1 occurs more than once.</markdown> </div> <p style="font-weight: bold;">Example 2:</p> <div style="margin-left: 20px;"> <markdown>Input: `nums = {1, 2, 3}`</markdown> <markdown>Output: `false`</markdown> <markdown>Explanation: All elements are unique.</markdown> </div> <p style="font-weight: bold;">You may assume:</p> <div style="margin-left: 20px;"> <markdown>`nums` will never be `null`.</markdown> </div> <java-quick-reference></java-quick-reference> <pl-file-editor file-name="Solution.java" ace-mode="ace/mode/java">public class Solution { public boolean containsDuplicate(int[] nums) { // Type code here. Do not change the class or method headers. } }</pl-file-editor> </pl-question-panel> <pl-submission-panel> <pl-external-grader-results></pl-external-grader-results> <pl-file-preview></pl-file-preview> </pl-submission-panel>
-
If you do not have any additional Java classes required for this question (e.g. a
Node
class for a linked list question), then you can skip this step. To add additional Java classes, create a folder calledlibs
inside thetests
folder of this question (such that it is on the same level as thejunit
folder. Go back into IntelliJ and go into theout/production/ProjectName
folder and find the corresponding .class file of the class that needs to be included but not edited. Open that .class file in your file explorer and copy it into the newly createdlibs
folder. Repeat this with all .class files needed. Do not copy overTests.class
,Solution.class
, or the name of the file the student will edit. Note that the students won’t be able to see the implementation of these files, so you should display the contents of the file or a UML class diagram insidequestion.html
(see question.html Formatting). -
To preview your file, go into the “Questions” tab in your locally hosted PrairieLearn site, and find the question you just wrote. Ensure it is formatted correctly, and try the “Save & Grade” button to ensure the autograder works properly. It should only show the results of the example test cases.
-
Once you are satisfied with the question, be sure to delete the solution from
question.html
. In the locally hosted PrairieLearn site, go to the question settings and update the topic and tags to match the question and click “Save”. Then, commit and push your changes. Now, your questions should be synced to the remote repository and available on the online PrairieLearn course site!