In this guide, we explore the advanced grading options available for C# assignments. For more information about setting up a C# assignment from scratch, see:
Using the .NET framework, we compile the students' files using the
command, which produces an output that potentially contains compilation errors or warnings. We can report and highlight these inline within the student submission using the custom CodeGrade command cg comments .
For example, to build a dotnet console project, we can use a Script Block that runs the following commands:
cd~/console_project# copy the student's files into the project directorycp $STUDENT/*.cs.# save the output produced when building the projectOUTPUT=$(dotnetbuild--disable-build-servers/clp:NoSummary)# use the cg comments command to produce inline commentsecho"$OUTPUT"|cgcommentsgeneric \'^(?P<file>[^\(]*)\((?P<line>\d+),(?P<column>\d+)\):\s*(?P<severity>error|warning|info)\s+(?P<code>CS\d+):(?P<message>[^[]*).*$' \--severities'Error:error,Warning:warning' \--origin"build" \--ignore-parser-errors# exit the script according to the outcome of the build commandecho"$OUTPUT"|sed'/warning/d'if [[ "$OUTPUT"==*"error"* ]]; thenexit1fi
This will result in compilation errors or warnings being visible to the students within the student submission folder, as shown below:
Unit Tests with xUnit
xUnit is the standard unit testing tool within the .NET Framework and can be used to test .NET languages such as C#. xUnit is especially appropriate for grading assignments that require students to write functions and classes. xUnit unit tests offer several advantages over conventional IO tests including the ability to use assertions, parametrize test cases, and provide custom feedback for students. For an overview of the testing attributes available with xUnit, we recommend this reference.
Consider the following Calculator.cs submission that implements a simple arithmetic calculator:
usingSystem;publicclassCalculator{publicdoubleAdd(double a,double b) {return a + b; }publicdoubleSubtract(double a,double b) {return a - b; }publicdoubleMultiply(double a,double b) {return a * b; }publicdoubleDivide(double a,double b) {if (b ==0) {Console.WriteLine("Error: Division by zero is not allowed.");returndouble.NaN; }return a / b; }}
Our tests are defined in the following CalculatorTests.cs file:
usingXunit;usingSystem;namespaceCalculatorTests{publicclassCalculatorTests {privateCalculator calculator =newCalculator(); [Fact(DisplayName ="[5] Addition Test")]publicvoidAdditionTest() {double result =calculator.Add(5,3);Assert.Equal(8, result); } [Fact(DisplayName ="[3] Subtraction Test")]publicvoidSubtractionTest() {double result =calculator.Subtract(5,3);Assert.Equal(1, result); } [Fact(DisplayName ="[5] Multiplication Test")]publicvoidMultiplicationTest() {double result =calculator.Multiply(5,3);Assert.Equal(15, result); } [Fact(DisplayName ="[3] Division Test")]publicvoidDivisionTest() {double result =calculator.Divide(6,3);Assert.Equal(2, result); } [Fact(DisplayName ="Division by Zero Test")]publicvoidDivisionByZeroTest() {Assert.Equal(double.NaN,calculator.Divide(6,0)); } }}
As displayed above, we can use the xUnit's Fact attribute to set a custom name for each test. Moreover, by using naming the test as "[n] My Custom Test Name", we can also set a custom weight equal to n , with the default weight being 1.
Setup
In order to run unit tests with xUnit, in the Setup Phase we first have to install dotnet using the corresponding block and create an xUnit project.
After installing dotnet, use a Script Block and type the following bash script:
# create a folder with an xUnit projectmkdirxunit_projectcdxunit_projectdotnetnewxunit# remove the template testing class created by defaultrmUnitTest1.cs# install tool to create a test xml report in the junit formatdotnetaddpackageXunitXml.TestLogger--version3.1.20dotnettoolinstall--globaldotnet-xunit-to-junit--version6.0.0# make the installed tool available from the command line by adding it to the pathecho'export PATH=$PATH:~/.dotnet/tools'>>~/.cg_bash_env
If the class you want to test contains a main method you should add the following line at the bottom of the script:
# Modify the xUnit project configuration so that dotnet does not create an additional Main Method for the Unit Test classsed-i'/<PropertyGroup>/a <GenerateProgramFile>false</GenerateProgramFile>'xunit_project.csproj
Tests
Project Build
Once we are in the Tests phase, before running the actual tests, we have to:
Upload the fixture file that defines the xUnit tests;
Move both the xUnit testing file and the student submission files to the xUnit project folder;
Compile the student's code by building the xUnit Project.
After using an Upload File block to upload the xUnit testing file, use a Script Block to run the following script:
# move to the xunit project foldercd~/xunit_project# add both the submission and testing files to the xUnit projectcp $STUDENT/*.cs.cp $FIXTURES/*.cs.# build the projectOUTPUT=$(dotnetbuild--disable-build-servers/clp:NoSummary)# display compilation comments inlineecho"$OUTPUT"|sed's|/Files|/Student|g'|cgcommentsgeneric \'^(?P<file>[^\(]*)\((?P<line>\d+),(?P<column>\d+)\):\s*(?P<severity>error|warning|info)\s+(?P<code>CS\d+):(?P<message>[^[]*).*$' \--severities'Error:error,Warning:warning' \--origin"build" \--ignore-parser-errors# exit the script according to the outcome of the build commandecho"$OUTPUT"|sed'/warning/d'if [[ "$OUTPUT"==*"error"* ]]; thenexit1fi
Notice that:
you can hide the script's content from the student using a Hide block as shown in the image above.
we can report potential compilation errors inline into the student's submission, as explained at the top of this page.
Test execution
We are finally ready to run our tests. For this, we can use a Custom Test Block.
The Custom Test Block runs the following commands:
# move to the xUnit project foldercd~/xunit_project# run the xUnit Tests and log the test results in junit xml formatdotnettest--disable-build-servers--logger:"xunit;LogFilePath=results.xml"# convert the xml in the junit formatdotnetxunit-to-junit"results.xml""junitResults.xml"# use the cg command to parse the test result and display the result to the studentcgjunitxmljunitResults.xml# shut down the dotnet background processdotnetbuild-servershutdown
As shown in the image above, you can optionally:
hide the configuration of the Custom Test Block from the student;
run the tests only if the Compilation step succeeded using a Run-If Block.
The results of the tests will be shown to the student as below: