Unit testing C# with NUnit and .NET Core
This tutorial takes you through an interactive experience building a sample solution step-by-step to learn unit testing concepts. If you prefer to follow the tutorial using a pre-built solution, view or download the sample code before you begin. For download instructions, see Samples and Tutorials.
This article is about testing a .NET Core project. If you're testing an ASP.NET Core project, see Integration tests in ASP.NET Core.
Prerequisites
- .NET 8.0 or later versions.
- A text editor or code editor of your choice.
Creating the source project
Open a shell window. Create a directory called unit-testing-using-nunit to hold the solution. Inside this new directory, run the following command to create a new solution file for the class library and the test project:
dotnet new sln
Next, create a PrimeService directory. The following outline shows the directory and file structure so far:
/unit-testing-using-nunit
unit-testing-using-nunit.sln
/PrimeService
Make PrimeService the current directory and run the following command to create the source project:
dotnet new classlib
Rename Class1.cs to PrimeService.cs. You create a failing implementation of the PrimeService
class:
using System;
namespace Prime.Services
{
public class PrimeService
{
public bool IsPrime(int candidate)
{
throw new NotImplementedException("Please create a test first.");
}
}
}
Change the directory back to the unit-testing-using-nunit directory. Run the following command to add the class library project to the solution:
dotnet sln add PrimeService/PrimeService.csproj
Creating the test project
Next, create the PrimeService.Tests directory. The following outline shows the directory structure:
/unit-testing-using-nunit
unit-testing-using-nunit.sln
/PrimeService
Source Files
PrimeService.csproj
/PrimeService.Tests
Make the PrimeService.Tests directory the current directory and create a new project using the following command:
dotnet new nunit
The dotnet new command creates a test project that uses NUnit as the test library. The generated template configures the test runner in the PrimeService.Tests.csproj file:
<ItemGroup>
<PackageReference Include="nunit" Version="4.3.1" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.5.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
Note
Prior to .NET 9, the generated code may reference older versions of the NUnit test framework. You may use dotnet CLI to update the packages. Alternatively, open the PrimeService.Tests.csproj file and replace the contents of the package references item group with the code above.
The test project requires other packages to create and run unit tests. The dotnet new
command in the previous step added the Microsoft test SDK, the NUnit test framework, and the NUnit test adapter. Now, add the PrimeService
class library as another dependency to the project. Use the dotnet add reference
command:
dotnet add reference ../PrimeService/PrimeService.csproj
You can see the entire file in the samples repository on GitHub.
The following outline shows the final solution layout:
/unit-testing-using-nunit
unit-testing-using-nunit.sln
/PrimeService
Source Files
PrimeService.csproj
/PrimeService.Tests
Test Source Files
PrimeService.Tests.csproj
Execute the following command in the unit-testing-using-nunit directory:
dotnet sln add ./PrimeService.Tests/PrimeService.Tests.csproj
Creating the first test
You write one failing test, make it pass, and then repeat the process. In the PrimeService.Tests directory, rename the UnitTest1.cs file to PrimeService_IsPrimeShould.cs and replace its entire contents with the following code:
using NUnit.Framework;
using Prime.Services;
namespace Prime.UnitTests.Services
{
[TestFixture]
public class PrimeService_IsPrimeShould
{
private PrimeService _primeService;
[SetUp]
public void SetUp()
{
_primeService = new PrimeService();
}
[Test]
public void IsPrime_InputIs1_ReturnFalse()
{
var result = _primeService.IsPrime(1);
Assert.That(result, Is.False, "1 should not be prime");
}
}
}
The [TestFixture]
attribute denotes a class that contains unit tests. The [Test]
attribute indicates a method is a test method.
Save this file and execute the dotnet test
command to build the tests and the class library and run the tests. The NUnit test runner contains the program entry point to run your tests. dotnet test
starts the test runner using the unit test project you've created.
Your test fails. You haven't created the implementation yet. Make the test pass by writing the simplest code in the PrimeService
class that works:
public bool IsPrime(int candidate)
{
if (candidate == 1)
{
return false;
}
throw new NotImplementedException("Please create a test first.");
}
In the unit-testing-using-nunit directory, run dotnet test
again. The dotnet test
command runs a build for the PrimeService
project and then for the PrimeService.Tests
project. After you build both projects, it runs this single test. It passes.
Adding more features
Now that you've made one test pass, it's time to write more. There are a few other simple cases for prime numbers: 0, -1. You could add new tests with the [Test]
attribute, but that quickly becomes tedious. There are other NUnit attributes that enable you to write a suite of similar tests. A [TestCase]
attribute is used to create a suite of tests that execute the same code but have different input arguments. You can use the [TestCase]
attribute to specify values for those inputs.
Instead of creating new tests, apply this attribute to create a single data-driven test. The data driven test is a method that tests several values less than two, which is the lowest prime number:
[TestCase(-1)]
[TestCase(0)]
[TestCase(1)]
public void IsPrime_ValuesLessThan2_ReturnFalse(int value)
{
var result = _primeService?.IsPrime(value);
Assert.That(result, Is.False, $"{value} should not be prime");
}
Run dotnet test
, and two of these tests fail. To make all of the tests pass, change the if
clause at the beginning of the Main
method in the PrimeService.cs file:
if (candidate < 2)
Continue to iterate by adding more tests, theories, and code in the main library. You have the finished version of the tests and the complete implementation of the library.
You've built a small library and a set of unit tests for that library. You've also structured the solution so that adding new packages and tests is part of the standard workflow. You've concentrated most of your time and effort on solving the goals of the application.