{"id":13305840,"url":"https://github.com/tkutcher/jgrade","last_synced_at":"2026-01-16T11:25:16.115Z","repository":{"id":45150794,"uuid":"164700318","full_name":"tkutcher/jgrade","owner":"tkutcher","description":"A library for grading Java assignments","archived":false,"fork":false,"pushed_at":"2023-01-08T01:12:42.000Z","size":663,"stargazers_count":23,"open_issues_count":3,"forks_count":13,"subscribers_count":0,"default_branch":"dev","last_synced_at":"2023-07-27T21:45:08.789Z","etag":null,"topics":["autograder","autograders","grade","gradescope","java-8"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tkutcher.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-08T17:30:52.000Z","updated_at":"2023-07-27T21:45:08.790Z","dependencies_parsed_at":"2023-02-08T04:01:24.382Z","dependency_job_id":null,"html_url":"https://github.com/tkutcher/jgrade","commit_stats":null,"previous_names":[],"tags_count":4,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkutcher%2Fjgrade","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkutcher%2Fjgrade/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkutcher%2Fjgrade/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tkutcher%2Fjgrade/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tkutcher","download_url":"https://codeload.github.com/tkutcher/jgrade/tar.gz/refs/heads/dev","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242868459,"owners_count":20198484,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["autograder","autograders","grade","gradescope","java-8"],"created_at":"2024-07-29T17:54:36.684Z","updated_at":"2026-01-16T11:25:16.087Z","avatar_url":"https://github.com/tkutcher.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# JGrade\n_A library for grading Java assignments_\n\n\n[![pipeline status](https://gitlab.com/tkutcher/jgrade/badges/dev/pipeline.svg)](https://gitlab.com/tkutcher/jgrade/pinelines/dev/latest)\n\u003ca href=\"https://tkutcher.gitlab.io/jgrade/api\"\u003e\n    \u003cimg src=\"https://img.shields.io/static/v1?label=%20\u0026message=docs\u0026color=informational\" alt=\"docs\"/\u003e\n\u003c/a\u003e\n\u003ca href=\"https://tkutcher.gitlab.io/jgrade\"\u003e\n    \u003cimg src=\"https://img.shields.io/static/v1?label=version\u0026message=1.1.0\u0026color=orange\" alt=\"version\"/\u003e\n\u003c/a\u003e\n\n\n\u003cbr\u003e\n\n\n\n[API Documentation](https://tkutcher.gitlab.io/jgrade/api)\n\n\n---\n\n\nNOTE - I've moved the CI to GitLab and am using GitLab to host the API docs (https://tkutcher.gitlab.io/jgrade), but this\nwill remain the primary repository. GitLab will just mirror the master and dev branches.\n\n\n:bangbang: Help Wanted :bangbang:\n\nOnce upon a time, it was my priority to be grading intermediate-level Java 8 assignments - but after graduating that\npriority has gone down a bit :grimacing: . I would be happy to help familiarize anyone who is interested in contributing\nand keeping this maintained. Submit an issue in the project if you are interested or have any ideas!\n\n\n- [Overview](#overview)\n- [Quick Start](#quick-start)\n- [Features and Usage](#features-and-usage)\n- [Development](#development)\n  - [Ideas / Wishlist](#wishlist)\n \n---\n\n## Overview\nJGrade is a helper tool with various classes designed to assist in course instructors \"autograding\" an assignment, \ninspired by the [Gradescope Autograder](https://gradescope-autograders.readthedocs.io/en/latest/). There are classes \nthat the client can integrate with directly, or use the jar's main method (and provide a class with annotations) that \nwraps a lot of common functionality (see [examples](https://github.com/tkutcher/jgrade/tree/development/examples)). \nIt was designed to produce the output needed for Gradescope while being extensible enough to produce different \noutputs and configure the specific JSON output Gradescope is looking for.\n\n\n## Quick Start\n\nTo make use of this, you first need to grab the jar file from the [Releases](https://github.com/tkutcher/jgrade/releases) page.\nThis includes many classes you can make use of, as well as a main method for running and producing grading output.\n\nWith this, you could have the following setup:\n\nA class that runs some unit tests we want to treat their success as a grade (these would import student code):\n\n```java\nimport com.github.tkutcher.jgrade.gradedtest.GradedTest;\nimport org.junit.Test;\n\nimport static com.github.tkutcher.jgrade.gradedtest.GradedTestResult.HIDDEN;\nimport static com.github.tkutcher.jgrade.gradedtest.GradedTestResult.VISIBLE;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\nimport static org.junit.Assert.fail;\n\npublic class ExampleGradedTests {\n    @Test\n    @GradedTest(name=\"True is true\", points=2.0, visibility=VISIBLE)\n    public void trueIsTrue() {\n        assertTrue(true);\n    }\n\n    @Test\n    @GradedTest(name=\"False is false\", number=\"2\", points=3.0, visibility=HIDDEN)\n    public void falseIsFalse() {\n        assertFalse(false);\n    }\n\n    @Test\n    @GradedTest(name=\"Captures output\")\n    public void capturesOutput() {\n        System.out.println(\"hello\");\n    }\n\n    @Test\n    @GradedTest(name=\"This test should fail\")\n    public void badTest() {\n        fail();\n    }\n}\n```\n\nand a main method with some other grading-related non-unit-testing logic `MyGrader.java`:\n\n```java\nimport com.github.tkutcher.jgrade.BeforeGrading;\nimport com.github.tkutcher.jgrade.AfterGrading;\nimport com.github.tkutcher.jgrade.Grade;\nimport com.github.tkutcher.jgrade.Grader;\nimport com.github.tkutcher.jgrade.gradedtest.GradedTestResult;\n\nimport static com.github.tkutcher.jgrade.gradedtest.GradedTestResult.HIDDEN;\n\n\npublic class BasicGraderExample {\n\n    /* All @Grade/@BeforeGrading/@AfterGrading methods must take exactly one parameter\n     * of type Grader. This parameter is the same grader throughout.\n     *\n     * @BeforeGrading methods are run before others.\n     */\n    @BeforeGrading\n    public void initGrader(Grader grader) {\n        grader.startTimer();\n    }\n\n    /* You can run unit tests that are annotated with @GradedTest to add\n     * GradedTestResults to the Grader in this way.\n     */\n    @Grade\n    public void runGradedUnitTests(Grader grader) {\n        grader.runJUnitGradedTests(ExampleGradedTests.class);\n    }\n\n    /* You can also manually add GradedTestResults you create to the grader. */\n    @Grade\n    public void singleTestResult(Grader grader) {\n        grader.addGradedTestResult(\n                new GradedTestResult(\"manual test\", \"1\", 1.0, HIDDEN)\n        );\n    }\n\n    /* Grader.startTimer() and Grader.stopTimer() can be used to time the grader */\n    @Grade\n    public void loopForTime(Grader grader) {\n        long startTime = System.currentTimeMillis();\n        while (System.currentTimeMillis() - startTime \u003c 1000);\n    }\n\n    /* @AfterGrading methods are run after all other methods. */\n    @AfterGrading\n    public void endGrader(Grader grader) {\n        grader.stopTimer();\n    }\n}\n```\n\nThen, you could run \n\n```shell script\njava -jar ../lib/jgrade-1.1-all.jar -c MyGrader -o results.json\n```\n\nand get GradeScope-formatted json. See the [examples](/examples) for more complete examples and how to set up a script\nto work with GradeScope, and expand the usage below to see the arguments you can provide this main program.\n\n\u003cdetails\u003e\u003csummary\u003eUsage\u003c/summary\u003e\n\u003cp\u003e\n\n```\n-c,--classname arg            the class containing annotated methods to grade\n-f,--format output-format     specify output, one of 'json' (default) or 'txt'\n-h,--help\u003cbr\u003e\n   --no-output                don't produce any output (if user overriding)\n-o destination                save output to another file (if not specified,\n                              prints to standard out)\n   --pretty-print             pretty-print output (when format is json)\n-v,--version\n\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n  \n## Features and Usage\n\nThe way I used this library is to have a base class for the course (for example, a `_226Grader`) that contains \nannotated methods for functionality/grading parts that are consistent across all assignments. For example, the \n`@BeforeGrading` method starts a timer and the `@AfterGrading` method stops it. There is a `@Grade` method that \ndoes the \"grading\" of style with checkstyle. Subclasses, for example `Assignment1Grader` (or `Assignment0Grader`\n I suppose :wink:), extend this and add  `@Grade` methods to add assignment-specific grading. \nSee the gradescope folder in the examples for a rough example setup.\n\n### Features\n\nSee the [API Docs](https://tkutcher.gitlab.io/jgrade/api) for more complete documentation.\n\n#### `CheckstyleGrader`\n\nWith the `CheckstyleGrader` you can specify grading deductions for checkstyle errors. This method below, for example,\nwould check the students files and deduct a point for each checkstyle error type (missing javadoc, require this, etc.).\n \n```java\n    @Grade\n    public void runCheckstyle(Grader grader) {\n        CheckstyleGrader checker = new CheckstyleGrader(5.0, 1.0, MY_CHECKSTYLE_JAR, STUDENTFILES);\n        checker.setConfig(MY_CHECKSTYLE_CONFIG);\n        GradedTestResult result = checker.runForGradedTestResult();\n        result.setScore(Math.max(0, 5 - checker.getErrorTypeCount()));\n        grader.addGradedTestResult(result);\n    }\n```\n\n#### `DeductiveGraderStrategy`\n\nYou can use this strategy to make failed tests deduct points from a total. So say in the current assignment there are two\nparts, A and B, each worth 25 points. If someone fails 30 tests for part B each worth one point, you don't want that to cut\nin to the assignment A portion:\n\n```java\npublic class GradeAssignment7 extends Grade226Assignment {\n    \n    private static final int AVL_POINTS = 30;\n    private static final int TREAP_POINTS = 20;\n\n    @Grade\n    public void gradeAvlTree(Grader grader) {\n        grader.setGraderStrategy(new DeductiveGraderStrategy(AVL_POINTS, \"AvlTreeMap\"));\n        grader.runJUnitGradedTests(GradeAvlTreeMap.class);\n    }\n\n    @Grade\n    public void gradeBinaryHeapPQ(Grader grader) {\n        grader.setGraderStrategy(new DeductiveGraderStrategy(TREAP_POINTS, \"TreapMap\"));\n        grader.runJUnitGradedTests(GradeTreapMap.class);\n    }\n}\n```\n\n\n#### `DeductiveGraderStrategy`\n\nYou can use this strategy to make failed tests deduct points from a total. So say in the current assignment there are two\nparts, A and B, each worth 25 points. If someone fails 30 tests for part B each worth one point, you don't want that to cut\nin to the assignment A portion:\n\n```java\npublic class GradeAssignment7 extends Grade226Assignment {\n    \n    private static final int AVL_POINTS = 30;\n    private static final int TREAP_POINTS = 20;\n\n    @Grade\n    public void gradeAvlTree(Grader grader) {\n        grader.setGraderStrategy(new DeductiveGraderStrategy(AVL_POINTS, \"AvlTreeMap\"));\n        grader.runJUnitGradedTests(GradeAvlTreeMap.class);\n    }\n\n    @Grade\n    public void gradeBinaryHeapPQ(Grader grader) {\n        grader.setGraderStrategy(new DeductiveGraderStrategy(TREAP_POINTS, \"TreapMap\"));\n        grader.runJUnitGradedTests(GradeTreapMap.class);\n    }\n}\n```\n\n#### `CLITester`\n\nA class to help wrap testing command line programs. You subclass `CLITester`, then implement\nthe `getInvocation()` method for how the command line program is invoked, then you can use\n`runCommand(String)` to get the output in an object that you can test for expected output.\n\n\n---\n\n## Development\n\n- `mvn install` to compile\n- `mvn test` to run unit tests\n- `mvn checkstyle:checkstyle` to run checkstyle\n- `mvn javadoc:jar` to generate API docs.\n\nCheck out [contributing](/CONTRIBUTING.md) for more.\n\n\n### Requirements\nJGrade is written in [Java 8](https://www.oracle.com/technetwork/java/javase/overview/java8-2100321.html). \nSince the library has classes designed to run alongside JUnit, [JUnit 4](https://junit.org/junit4/) is a dependency \nfor the entire project (as opposed to just for running the projects own unit tests). \nThe [org.json](https://mvnrepository.com/artifact/org.json/json) package is used in producing correctly formatted \nJSON output, and the [Apache Commons CLI](https://commons.apache.org/proper/commons-cli/) library is used for \nreading the command line in the main program.\n\nFor simplicity, the main jar (appended with \"-all\") includes all of these dependencies.\n\n### Wishlist\n- Feedback for required files\n  - In our autograder, we built in something that took a list of required files and created a visible test case worth 0 points of what files were missing - this helped students debug.\n  - Could try and move some of this there.\n- Actual Observer pattern\n  - Allow for people to specify custom handlers whenever things like new graded test results are added\n  - Old \"observer\" terminology not really an observer\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftkutcher%2Fjgrade","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftkutcher%2Fjgrade","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftkutcher%2Fjgrade/lists"}