Dotnet Testing
The Dotnet test type is used to test C#/.NET workspaces. Tests are run using the NUnit framework.
Test Structure
To prepare the workspace for NUnit tests, include this code in the .csproj
file:
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
<PackageReference Include="NUnit" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.9.0" />
</ItemGroup>
A dotnet/NUnit "test" is a class containing multiple methods. Each method is a unit test. Use the below steps to set up every test file:
- Import the
NUnit.Framework
namespace - Define a namespace within the namespace used in the learner's file, usually
SomeNamespace.UnitTests
- Label the class with
[TestFixture]
- Label the methods with
[Test]
using System;
using NUnit.Framework;
namespace YOURNAMESPACE.UnitTests
{
[TestFixture]
public class Tests
{
[Test]
public void Some_Test()
{}
}
}
Every [Test]
method contains at least one assertion, which is defined using Assert.That()
. The method name does not affect execution but it should be named with its desired outcome, for example: Constructor_Sets_Name()
.
- Basic explanation of assertions
- List of constraints, which are used to define the type of assertion ("is the value equal to...", "is the value greater than...", "is the value of type...")
Example Tests
Test for Equality
This test uses an assertion of the form:
Assert.That(Object actual, Is.EqualTo(Object expected), string message);
Technically this method is a generic method, so the first parameter is not actually of type Object
but the type specified by the argument at runtime. All .NET objects inherit from Object
, so this type is used for simplicity. The point is: the method accepts any type.
This test assumes that there is a class Forest
and it tests that its constructor sets the Name
property.
using System;
using NUnit.Framework;
namespace YOURNAMESPACE.UnitTests
{
[TestFixture]
public class Tests
{
[Test]
public void Constructor_Sets_Name()
{
string newName = "Amazon";
Forest f = new Forest(newName);
Assert.That(f.Name, Is.EqualTo(newName), "The constructor should use its argument to set the `Name` property.");
}
}
}
Learn more about the equality constraint here.
Multiple Test Cases
This test uses the same equality constraint as above, but it tests multiple cases. Each value is defined in the [TestCase()]
attribute and is passed in as an argument to the test method.
using System;
using NUnit.Framework;
namespace YOURNAMESPACE.UnitTests
{
[TestFixture]
public class Tests
{
[Test]
[TestCase("Tropical")]
[TestCase("Temperate")]
[TestCase("Boreal")]
public void Biome_Accepts_ThreeValues(string biome)
{
Forest f = new Forest();
f.Biome = biome;
Assert.That(f.Biome, Is.EqualTo(biome), $"`Biome` should accept `\"{biome}\"` as a value.");
}
}
}
Learn more about test cases here.
Reflection (Properties)
This test uses the tools in the System.Reflection
namespace to make assertions on C# classes, structs, and members.
The general pattern is
1. Capture the type of a class using the typeof
operator.
2. Access the type's metadata using any method of the Type
class.
3. Make assertions on that type's metadata.
This test uses an assertion of this form:
Assert.That(Object anObject, Is.Not.Null, string message);
using System;
using System.Reflection;
using NUnit.Framework;
namespace YOURNAMESPACE.UnitTests
{
[TestFixture]
public class Tests
{
PropertyInfo pi = typeof(Forest).GetProperty("ForestsCreated", BindingFlags.Public | BindingFlags.Static);
[Test]
public void Forest_HasStaticProperty_ForestsCreated()
{
Assert.That(pi, Is.Not.Null, "Define a public static property `ForestsCreated`.");
}
[Test]
public void ForestsCreated_Has_PrivateSetter()
{
Assert.That(pi.GetSetMethod(nonPublic: true), Is.Not.Null, "Add a setter for `ForestsCreated`.");
Assert.That(pi.GetSetMethod(nonPublic: false), Is.Null, "Make the setter for `ForestsCreated` private.");
}
}
}
The GetProperty()
method is defined in the Type
class.
The GetSetMethod()
is defined in the PropertyInfo
class.
Reflection II (Methods)
This test also uses reflection, but makes assertions on a method.
using System;
using System.Reflection;
using NUnit.Framework;
namespace YOURNAMESPACE.UnitTests
{
[TestFixture]
public class Tests
{
MethodInfo mi = typeof(Forest).GetMethod("PrintTreeFacts", BindingFlags.Public | BindingFlags.Static);
[Test]
public void PrintTreeFacts_Is_Static()
{
Assert.That(mi, Is.Not.Null, "Define a public static method `PrintTreeFacts()`.");
}
[Test]
public void PrintTreeFacts_Is_Void()
{
Assert.That(mi.ReturnType, Is.EqualTo(typeof(void)), "`PrintTreeFacts()` should have a return type of `void`.");
}
}
}
Other Guidelines
- Actual code (real variable names, language syntax, etc. ) in feedback messages should be code-ticked (`)
- Dotnet tests should follow Test standards.