C#
We use NUnit 3 as a testing framework for C# coding exercises.
Hello, World!
Before we go into details, let's see how you can use NUnit to test the most basic C# task: writing a function that returns "Hello, World!".
This is what a student might write:
public class Hello
{
public static string HelloMessage()
{
return "Hello, World!";
}
}
And this is an example of what you could write to evaluate the student's code:
using Coding.Exercise;
using NUnit.Framework;
namespace Coding.Exercise.UnitTests
{
[TestFixture]
public class TestHello
{
[Test]
public void TestHelloMessage()
{
Assert.That(Hello.HelloMessage(),
Is.EqualTo("Hello, World!"),
"Your function must return: Hello, World!");
}
}
}
Let's examine the above evaluation code.
First, we must include 2 namespaces:
Coding.Exercise
, which is the student's code.NUnit.Framework
, which is the testing framework we use.
Although optional, it's a good practice to write your testing code inside a namespace, which you can name however you want. We named it Coding.Exercise.UnitTests
here.
[TestFixture]
Then we need to define a suite of unit tests as a public class with the TestFixture attribute.
This groups together multiple unit tests and allows implementing common functionality that you want executed before and after each test.
[Test]
Inside a [TestFixture] class, you must define public methods with the Test attribute. These are the only methods that will be executed when running your evaluation code. You don't need to implement a Main()
function.
Assert
Assertions are the statements that actually verify that the student's code behaves as expected. The most common pattern is to call student's methods and assert that their return value is the one that you expect. If it is not, then the test will fail and you have the opportunity to provide a custom message to the student for each failed assertion, so that she can improve.
NUnit 3 introduced a new assertion model and they encourage using only this one. It's called the Constraint Model. The most common way that you will use it is:
Assert.That(valueThatYouWantToVerify, constraint, feedbackMessage);
or the more generic
Assert.That(booleanCondition, feedbackMessage);
which allows you to pass in any boolean condition that you compute to test student code.
In our previous example we used the EqualConstraint, but you can find the full list of constraints here.
Standard output
In the previous example we had the students return Hello, World!
, but actually a more common task is to have them print the same string to the standard output.
public class Hello
{
public static void SayHello()
{
System.Console.WriteLine("Hello, World!");
}
}
How do we test this specific function?
using System;
using System.IO;
using Coding.Exercise;
using NUnit.Framework;
namespace Coding.Exercise.UnitTests
{
[TestFixture]
public class TestHello
{
[Test]
public void TestSayHello()
{
using (StringWriter sw = new StringWriter())
{
// Save a reference to the standard output.
TextWriter stdout = Console.Out;
// Redirect standard output to variable.
Console.SetOut(sw);
// Call student's code
Hello.SayHello();
// Restore the original standard output.
Console.SetOut(stdout);
// Assert
Assert.That(sw.ToString(), Is.EqualTo("Hello, World!\n"));
}
}
}
}
You can use the above pattern in your own evaluation code whenever you need to verify what students are printing to standard output. We are basically redirecting standard output to a variable that we can use to verify after running the student's code. Just don't forget to restore the standard output!
Technical specs
We use .NET Core on a Ubuntu machine to run the code.
Example 1: FizzBuzz
Given:
An integer number n
Your task is to:
Write a function that returns an array with the numbers from 1 to n with the following restrictions:
for multiples of 3 store "Fizz" instead of the number
for multiples of 5 store "Buzz" instead of the number
for numbers which are multiples of both 3 and 5 store "FizzBuzz"
Example:
fizzbuzz(15) == {
"1", "2", "Fizz", "4", "Buzz",
"Fizz", "7", "8", "Fizz", "Buzz",
"11", "Fizz", "13", "14", "FizzBuzz"
}
Here's how a student might solve the problem:
using System;
using System.Linq;
using System.Collections.Generic;
namespace Coding.Exercise
{
public class FizzBuzz
{
public static List<string> Compute(int n)
{
return Enumerable.Range(1, n)
.Select(a => String.Format("{0}{1}",
a % 3 == 0 ? "Fizz" : string.Empty,
a % 5 == 0 ? "Buzz" : string.Empty))
.Select((b, i) => String.IsNullOrEmpty(b) ? (i + 1).ToString() : b)
.ToList();
}
}
}
Here's how you might test the student's solution:
using System.Collections.Generic;
using Coding.Exercise;
using NUnit.Framework;
namespace Coding.Exercise.UnitTests
{
[TestFixture]
public class TestFizzBuzz
{
[Test]
public void ReturnOne()
{
Assert.That(FizzBuzz.Compute(1),
Is.EqualTo(new List<string> {"1"}));
}
[Test]
public void ReturnFifteen()
{
int n = 15;
List<string> result = FizzBuzz.Compute(n);
List<string> expected = new List<string> {
"1", "2", "Fizz", "4", "Buzz",
"Fizz", "7", "8", "Fizz", "Buzz",
"11", "Fizz", "13", "14", "FizzBuzz"
};
Assert.That(result.Count,
Is.EqualTo(expected.Count),
string.Format("Your function should return {0} objects for n = {1}", n, n));
for (int i = 0; i < n; i++)
{
Assert.That(result[i],
Is.EqualTo(expected[i]),
string.Format("Your function should convert the value {0} to {1} instead of {2}",
i + 1, expected[i], result[i]));
}
}
}
}
Example 2: Title Case
Given:
A sentence with words separated by spaces
Your task is to:
Write a function that returns a copy of this sentence where all the words:
start with a capital letter
the rest of the letters are lower case
Example:
SentenceUtils.ToTitleCase("This is SO MUCH FUN! ") == "This Is So Much Fun! "
Here's how a student might solve the problem:
using System;
using System.Text;
namespace Coding.Exercise
{
public class SentenceUtils
{
public static string ToTitleCase(string sentence)
{
if (sentence == null || sentence.Length == 0)
{
return sentence;
}
StringBuilder result = new StringBuilder(sentence);
result[0] = char.ToUpper(result[0]);
for (int i = 1; i < result.Length; i++)
{
result[i] = char.IsWhiteSpace(result[i - 1]) ? char.ToUpper(result[i]) : char.ToLower(result[i]);
}
return result.ToString();
}
}
}
Here's how you might test the student's solution:
using Coding.Exercise;
using NUnit.Framework;
namespace Coding.Exercise.UnitTests
{
[TestFixture]
public class TestSentenceUtils
{
[Test]
public void Character()
{
Assert.That(SentenceUtils.ToTitleCase("a"),
Is.EqualTo("A"),
"Your function should convert a single character.");
Assert.That(SentenceUtils.ToTitleCase("A"),
Is.EqualTo("A"),
"Your function should convert a single character.");
}
[Test]
public void Word()
{
Assert.That(SentenceUtils.ToTitleCase("abc"),
Is.EqualTo("Abc"),
"Your function should convert a single word.");
Assert.That(SentenceUtils.ToTitleCase("ABC"),
Is.EqualTo("Abc"),
"Your function should convert a single word.");
Assert.That(SentenceUtils.ToTitleCase("aBC"),
Is.EqualTo("Abc"),
"Your function should convert a single word.");
Assert.That(SentenceUtils.ToTitleCase("Abc"),
Is.EqualTo("Abc"),
"Your function should convert a single word.");
}
[Test]
public void Sentence()
{
Assert.That(SentenceUtils.ToTitleCase("abc def ghi"),
Is.EqualTo("Abc Def Ghi"),
"Your function should convert a sentence.");
}
[Test]
public void MultipleWhitespace()
{
Assert.That(SentenceUtils.ToTitleCase(" abc def ghi "),
Is.EqualTo(" Abc Def Ghi "),
"Your function should keep multiple whitespaces.");
}
}
}
Last updated