TestKase Docs
AutomationTest Frameworks

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.Tests

This 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.Sdk

For NUnit XML output, also install the logger:

dotnet add package NunitXml.TestLogger

Project 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.cs

Writing 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, Description("[48271] ValidLogin")]
        public void ValidLogin()
        {
            var result = _loginService.Login("user@example.com", "password123");
            Assert.That(result.Success, Is.True);
            Assert.That(result.Token, Is.Not.Null);
        }

        [Test, Description("[48272] InvalidPassword")]
        public void InvalidPassword()
        {
            var result = _loginService.Login("user@example.com", "wrong");
            Assert.That(result.Success, Is.False);
            Assert.That(result.Error, Is.EqualTo("Invalid credentials"));
        }

        [Test, Description("[48273] EmptyEmailThrowsException")]
        public void 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)]
        [Description("[48274] LoginWithMultipleUsers")]
        public void LoginWithMultipleUsers(string email, string password, bool expected)
        {
            var result = _loginService.Login(email, password);
            Assert.That(result.Success, Is.EqualTo(expected));
        }
    }
}

Each test includes a 5-digit Automation ID in its [Description] attribute using square brackets. The @testkase/reporter CLI extracts these IDs using the regex \[(\d{5})\]. For the example above:

  • 48271 → linked to the "valid login" test case in TestKase
  • 48272 → linked to the "invalid password" test case in TestKase
  • 48273 → linked to the "empty email" test case in TestKase
  • 48274 → linked to the "multiple users" test case in TestKase

Generate Automation IDs in TestKase first, then embed them in your [Description] attributes. The [XXXXX] pattern can appear anywhere in the description string.

Running Tests

Run all tests:

dotnet test

Run with verbose output:

dotnet test --verbosity normal

Generating 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-results

This 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 \
  --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 NUnit test descriptions using the [XXXXX] bracket pattern:

Test PatternWhere to Embed the IDExtracted ID
Standard test[Test, Description("[48271] ValidLogin")]48271
Parameterized with [TestCase][Description("[48274] LoginWithMultipleUsers")]48274
Nested namespace[Test, Description("[48275] ValidateToken")]48275

The [XXXXX] pattern can appear anywhere in the [Description] attribute. 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, Description("[48271] ValidLogin")]
        public void ValidLogin()
        {
            var result = _loginService.Login("user@example.com", "password123");
            Assert.That(result.Success, Is.True);
        }

        [Test, Description("[48272] InvalidPassword")]
        public void 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-results

3. Report Results to TestKase

npx @testkase/reporter report \
  --token $TESTKASE_PAT \
  --project-id PRJ-1 \
  --org-id 1173 \
  --cycle-id TCYCLE-5 \
  --format nunit \
  --results-file test-results/nunit-results.xml

Troubleshooting

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.TestLogger

After installing, rebuild and run again:

dotnet build
dotnet test --logger "nunit;LogFileName=nunit-results.xml" --results-directory test-results

Test 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 NUnit3TestAdapter

Also 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.Sdk

Wrong 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.