NUnit
Set up NUnit for .NET testing and integrate test results with TestKase.
Overview
NUnit is the most popular unit testing framework for .NET applications. It provides a rich set of assertions, parameterized tests, setup/teardown lifecycle hooks, and category-based test filtering. NUnit 3 is the current major version and produces a well-defined XML output format.
To integrate NUnit results with TestKase, generate NUnit 3 XML output and report with --format nunit.
Prerequisites
- .NET SDK 6.0+ (or .NET 8.0+ recommended)
Installation
New Project
Create a new NUnit test project using the built-in template:
dotnet new nunit -n MyProject.TestsThis scaffolds a project with all required NUnit packages pre-configured.
Existing Project
Add NUnit packages to an existing test project:
dotnet add package NUnit
dotnet add package NUnit3TestAdapter
dotnet add package Microsoft.NET.Test.SdkFor NUnit XML output, also install the logger:
dotnet add package NunitXml.TestLoggerProject Setup
Your .csproj file should include these package references:
<!-- MyProject.Tests.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="NUnit" Version="4.1.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageReference Include="NunitXml.TestLogger" Version="3.1.20" />
</ItemGroup>
</Project>Directory Structure
MyProject.Tests/
MyProject.Tests.csproj
LoginTests.cs
DashboardTests.cs
Helpers/
TestData.csWriting Tests
Create a test file (e.g., LoginTests.cs):
// LoginTests.cs
using NUnit.Framework;
namespace MyProject.Tests
{
[TestFixture]
public class LoginTests
{
private LoginService _loginService;
[SetUp]
public void SetUp()
{
_loginService = new LoginService();
}
[TearDown]
public void TearDown()
{
_loginService = null;
}
[Test]
public void Test_48271_ValidLogin()
{
var result = _loginService.Login("user@example.com", "password123");
Assert.That(result.Success, Is.True);
Assert.That(result.Token, Is.Not.Null);
}
[Test]
public void Test_48272_InvalidPassword()
{
var result = _loginService.Login("user@example.com", "wrong");
Assert.That(result.Success, Is.False);
Assert.That(result.Error, Is.EqualTo("Invalid credentials"));
}
[Test]
public void Test_48273_EmptyEmailThrowsException()
{
Assert.That(
() => _loginService.Login("", "password123"),
Throws.ArgumentException.With.Message.Contains("Email is required")
);
}
[TestCase("user@example.com", "password123", true)]
[TestCase("admin@example.com", "admin456", true)]
[TestCase("unknown@example.com", "wrong", false)]
public void Test_48274_LoginWithMultipleUsers(string email, string password, bool expected)
{
var result = _loginService.Login(email, password);
Assert.That(result.Success, Is.EqualTo(expected));
}
}
}Each test embeds a 5-digit Automation ID in its method name using underscores (e.g.
Test_48271_ValidLogin). The NUnit parser reads only each test case's name/fullname attribute —
the [Description] attribute is emitted as a <property> child element that the reporter never reads —
so the ID must live in the method name. Configure the reporter with --automation-id-format "_(\\d{5})_"
to extract the ID from between the underscores. For the example above:
48271→ linked to the "valid login" test case in TestKase48272→ linked to the "invalid password" test case in TestKase48273→ linked to the "empty email" test case in TestKase48274→ linked to the "multiple users" test case in TestKase
The NUnit 3 results file exposes each test as <test-case name="..." fullname="...">, and the reporter
matches against those attributes only. IDs placed in [Description("[48271] ...")] become a
<property> child and are not read — they will never match. Put the ID in the method name and pass
--automation-id-format "_(\\d{5})_".
Generate Automation IDs in TestKase first, then embed them in your test method names.
Running Tests
Run all tests:
dotnet testRun with verbose output:
dotnet test --verbosity normalGenerating NUnit XML Output
Use the NUnit XML logger to produce NUnit 3 format output:
dotnet test --logger "nunit;LogFileName=nunit-results.xml" --results-directory test-resultsThis writes the results to test-results/nunit-results.xml.
You can add the logger configuration to a .runsettings file or pass it as a command-line argument.
The --results-directory flag controls where the output file is written.
TestKase Integration
After generating the NUnit XML file, report results to TestKase:
npx @testkase/reporter report \
--token $TESTKASE_PAT \
--project-id PRJ-1 \
--org-id 1173 \
--cycle-id TCYCLE-5 \
--format nunit \
--automation-id-format "_(\\d{5})_" \
--results-file test-results/nunit-results.xml--cycle-id is optional. If not provided, results are reported to TCYCLE-1 — the master test cycle for the project.
Automation ID Mapping
The reporter extracts 5-digit Automation IDs from the NUnit method name using the
--automation-id-format "_(\\d{5})_" pattern (the ID wrapped in underscores):
| Test Pattern | Where to Embed the ID | Extracted ID |
|---|---|---|
| Standard test | public void Test_48271_ValidLogin() | 48271 |
Parameterized with [TestCase] | public void Test_48274_LoginWithMultipleUsers(...) | 48274 |
| Nested namespace | public void Test_48275_ValidateToken() | 48275 |
The parser reads the <test-case> name/fullname attribute only — the [Description] attribute is
ignored. For parameterized tests, all parameter variations share the same Automation ID.
Complete Example
1. Test File
// LoginTests.cs
using NUnit.Framework;
namespace MyProject.Tests
{
[TestFixture]
public class LoginTests
{
private LoginService _loginService;
[SetUp]
public void SetUp()
{
_loginService = new LoginService();
}
[Test]
public void Test_48271_ValidLogin()
{
var result = _loginService.Login("user@example.com", "password123");
Assert.That(result.Success, Is.True);
}
[Test]
public void Test_48272_InvalidPassword()
{
var result = _loginService.Login("user@example.com", "wrong");
Assert.That(result.Success, Is.False);
}
}
}2. Run Tests and Generate NUnit XML
dotnet test --logger "nunit;LogFileName=nunit-results.xml" --results-directory test-results3. Report Results to TestKase
npx @testkase/reporter report \
--token $TESTKASE_PAT \
--project-id PRJ-1 \
--org-id 1173 \
--cycle-id TCYCLE-5 \
--format nunit \
--automation-id-format "_(\\d{5})_" \
--results-file test-results/nunit-results.xmlTroubleshooting
NUnit logger not found
If you get an error like The test logger 'nunit' was not found, the NunitXml.TestLogger package is
not installed. Add it to your project:
dotnet add package NunitXml.TestLoggerAfter installing, rebuild and run again:
dotnet build
dotnet test --logger "nunit;LogFileName=nunit-results.xml" --results-directory test-resultsTest adapter not loaded
If tests are not discovered or you see No test is available, ensure the NUnit3TestAdapter package is
referenced in your .csproj:
dotnet add package NUnit3TestAdapterAlso verify that Microsoft.NET.Test.Sdk is installed — this is required for the dotnet test command
to discover and run NUnit tests:
dotnet add package Microsoft.NET.Test.SdkWrong XML format (NUnit 2 vs NUnit 3)
The TestKase reporter expects NUnit 3 XML format. If you are using an older NUnit 2 runner or a third-party logger that produces NUnit 2 format, the reporter will fail to parse the results.
To verify you are using NUnit 3 format, check the root element of the generated XML:
- NUnit 3:
<test-run>root element - NUnit 2:
<test-results>root element
If you see <test-results>, switch to the NunitXml.TestLogger package which produces the correct
NUnit 3 format. Do not use the older NUnit.ConsoleRunner unless you confirm it outputs NUnit 3 XML.
