Practical examples of C# unit testing with NUnit examples for 2025
Quick example of C# unit testing with NUnit examples
Let’s start with a simple, honest example of C# unit testing with NUnit examples before we layer in more advanced scenarios.
using NUnit.Framework;
public class Calculator
{
public int Add(int a, int b) => a + b;
}
[TestFixture]
public class CalculatorTests
{
[Test]
public void Add_TwoPositiveNumbers_ReturnsSum()
{
// Arrange
var calculator = new Calculator();
// Act
var result = calculator.Add(2, 3);
// Assert
Assert.That(result, Is.EqualTo(5));
}
}
This tiny snippet captures the Arrange–Act–Assert pattern that appears in most examples of C# unit testing with NUnit examples. You create the system under test, execute a method, and assert that the result matches your expectation.
Core patterns shown in real examples of C# unit testing with NUnit
Before we go deeper, it helps to call out the patterns you’ll see repeated across all the real examples:
- Test fixtures: Classes tagged with
[TestFixture]that group related tests. - Test methods: Methods tagged with
[Test]or[TestCase]that NUnit discovers and runs. - Assertions: Calls to
Assert.Thatand related helpers to verify behavior. - Setup/Teardown: Optional hooks (
[SetUp],[TearDown],[OneTimeSetUp],[OneTimeTearDown]) to initialize shared state.
Every example of C# unit testing with NUnit in this article is just a variation on that core structure.
Examples of C# unit testing with NUnit examples for basic logic
Let’s move beyond the trivial calculator and test something slightly more realistic: a discount service with a few edge cases.
public class DiscountService
{
public decimal ApplyDiscount(decimal price, int percentage)
{
if (price < 0) throw new ArgumentOutOfRangeException(nameof(price));
if (percentage < 0 || percentage > 100)
throw new ArgumentOutOfRangeException(nameof(percentage));
return price - (price * percentage / 100m);
}
}
[TestFixture]
public class DiscountServiceTests
{
private DiscountService _service = null!;
[SetUp]
public void SetUp()
{
_service = new DiscountService();
}
[Test]
public void ApplyDiscount_ZeroPercent_ReturnsOriginalPrice()
{
var result = _service.ApplyDiscount(100m, 0);
Assert.That(result, Is.EqualTo(100m));
}
[Test]
public void ApplyDiscount_FiftyPercent_ReturnsHalfPrice()
{
var result = _service.ApplyDiscount(200m, 50);
Assert.That(result, Is.EqualTo(100m));
}
[Test]
public void ApplyDiscount_InvalidPercentage_Throws()
{
Assert.That(
() => _service.ApplyDiscount(100m, 150),
Throws.TypeOf<ArgumentOutOfRangeException>());
}
}
These tests show how examples of C# unit testing with NUnit examples often combine happy paths with guard clauses. You’re not just testing normal behavior; you’re also documenting which inputs are rejected.
Parameterized tests: several examples include multiple inputs
Parameterized tests are one of the best examples of C# unit testing with NUnit examples that scale well. Instead of writing a separate method for each input, you can use [TestCase] to feed data into a single test.
[TestFixture]
public class PasswordValidatorTests
{
private PasswordValidator _validator = null!;
[SetUp]
public void SetUp()
{
_validator = new PasswordValidator();
}
[TestCase("short", false, TestName = "Too short")]
[TestCase("longenough", true, TestName = "Valid basic length")]
[TestCase("WithNumber1", true, TestName = "Includes number")]
[TestCase("NoNumber", false, TestName = "Missing number")]
public void IsValidPassword_VariousInputs_ReturnsExpected(
string password,
bool expected)
{
var isValid = _validator.IsValid(password);
Assert.That(isValid, Is.EqualTo(expected));
}
}
Here, several real examples of C# unit testing with NUnit examples are compressed into one readable method. The test names stay clear, and you can add more cases as bugs appear in production.
Async and await: modern examples of C# unit testing with NUnit
Most 2024–2025 C# codebases rely heavily on async I/O. NUnit handles async tests directly, which is one of the best examples of C# unit testing with NUnit examples keeping up with modern C#.
public interface IUserRepository
{
Task<User?> GetByEmailAsync(string email, CancellationToken ct = default);
}
public class AuthService
{
private readonly IUserRepository _users;
public AuthService(IUserRepository users)
{
_users = users;
}
public async Task<bool> LoginAsync(string email, string password, CancellationToken ct = default)
{
var user = await _users.GetByEmailAsync(email, ct);
if (user is null) return false;
return user.PasswordHash == password; // simplified on purpose
}
}
[TestFixture]
public class AuthServiceTests
{
[Test]
public async Task LoginAsync_UnknownUser_ReturnsFalse()
{
var repo = Substitute.For<IUserRepository>();
repo.GetByEmailAsync("missing@example.com", Arg.Any<CancellationToken>())
.Returns(Task.FromResult<User?>(null));
var service = new AuthService(repo);
var result = await service.LoginAsync("missing@example.com", "pwd");
Assert.That(result, Is.False);
}
}
This test uses async/await directly in the test method, which works cleanly with NUnit’s runner. In many teams, the best examples of C# unit testing with NUnit examples now include async tests as a default, not a special case.
Note: This example uses NSubstitute for mocking, but you can use any mocking library you prefer.
Mocking and dependency injection: real examples from layered apps
In real applications, you’re rarely testing a single class with no dependencies. Here’s an example of C# unit testing with NUnit that shows how to isolate a service using interfaces and a mock.
public interface IEmailSender
{
Task SendAsync(string to, string subject, string body);
}
public class NotificationService
{
private readonly IEmailSender _emailSender;
public NotificationService(IEmailSender emailSender)
{
_emailSender = emailSender;
}
public async Task SendWelcomeEmailAsync(string email)
{
if (string.IsNullOrWhiteSpace(email))
throw new ArgumentException("Email required", nameof(email));
var subject = "Welcome";
var body = "Thanks for signing up.";
await _emailSender.SendAsync(email, subject, body);
}
}
[TestFixture]
public class NotificationServiceTests
{
[Test]
public async Task SendWelcomeEmailAsync_ValidEmail_SendsEmail()
{
var sender = Substitute.For<IEmailSender>();
var service = new NotificationService(sender);
await service.SendWelcomeEmailAsync("user@example.com");
await sender.Received(1).SendAsync(
"user@example.com",
"Welcome",
"Thanks for signing up.");
}
}
This is the kind of example of C# unit testing with NUnit you’ll see in a typical ASP.NET Core application: services are wired with dependency injection, and tests verify interactions with dependencies rather than spinning up actual infrastructure.
Data‑driven tests with TestCaseSource: when examples include larger datasets
Sometimes you want your examples of C# unit testing with NUnit examples to pull from a larger dataset or computed inputs. [TestCaseSource] lets you feed tests from arrays, enumerables, or even external data.
public class TaxCalculator
{
public decimal Calculate(decimal income)
{
if (income <= 0) return 0m;
if (income <= 50000m) return income * 0.1m;
return income * 0.2m;
}
}
[TestFixture]
public class TaxCalculatorTests
{
private static readonly object[] TaxCases =
{
new object[] { 0m, 0m },
new object[] { 10_000m, 1_000m },
new object[] { 50_000m, 5_000m },
new object[] { 100_000m, 20_000m }
};
[TestCaseSource(nameof(TaxCases))]
public void Calculate_VariousIncomes_ReturnsExpectedTax(
decimal income,
decimal expectedTax)
{
var calc = new TaxCalculator();
var tax = calc.Calculate(income);
Assert.That(tax, Is.EqualTo(expectedTax));
}
}
This style is handy when your best examples include business rules that are easiest to understand as tables of inputs and outputs.
Testing exceptions and edge cases: defensive examples of C# unit testing with NUnit
Good tests don’t just cover happy paths. They also lock in how your code behaves when things go sideways.
public class FileNameParser
{
public string GetExtension(string fileName)
{
if (string.IsNullOrWhiteSpace(fileName))
throw new ArgumentException("File name is required", nameof(fileName));
var index = fileName.LastIndexOf('.');
if (index <= 0 || index == fileName.Length - 1)
return string.Empty;
return fileName[(index + 1)..];
}
}
[TestFixture]
public class FileNameParserTests
{
[Test]
public void GetExtension_NullOrEmpty_ThrowsArgumentException()
{
var parser = new FileNameParser();
Assert.That(
() => parser.GetExtension(""),
Throws.TypeOf<ArgumentException>());
}
[Test]
public void GetExtension_NoExtension_ReturnsEmptyString()
{
var parser = new FileNameParser();
var ext = parser.GetExtension("README");
Assert.That(ext, Is.EqualTo(string.Empty));
}
[Test]
public void GetExtension_NormalFile_ReturnsExtension()
{
var parser = new FileNameParser();
var ext = parser.GetExtension("photo.jpg");
Assert.That(ext, Is.EqualTo("jpg"));
}
}
These are the kinds of real examples that prevent regressions later when someone “simplifies” your parsing logic.
Integration‑style examples of C# unit testing with NUnit in 2024–2025
Pure unit tests are great, but many teams now mix in light integration tests: tests that touch a database or HTTP endpoint but still run quickly.
Here’s a trimmed‑down example using an in‑memory repository to mimic persistence:
public interface IOrderRepository
{
Task SaveAsync(Order order);
Task<Order?> GetAsync(Guid id);
}
public class InMemoryOrderRepository : IOrderRepository
{
private readonly Dictionary<Guid, Order> _store = new();
public Task SaveAsync(Order order)
{
_store[order.Id] = order;
return Task.CompletedTask;
}
public Task<Order?> GetAsync(Guid id)
{
_store.TryGetValue(id, out var order);
return Task.FromResult(order);
}
}
[TestFixture]
public class OrderRepositoryTests
{
[Test]
public async Task SaveAndGetAsync_PersistsOrderInMemory()
{
var repo = new InMemoryOrderRepository();
var order = new Order { Id = Guid.NewGuid(), Total = 123.45m };
await repo.SaveAsync(order);
var loaded = await repo.GetAsync(order.Id);
Assert.That(loaded, Is.Not.Null);
Assert.That(loaded!.Total, Is.EqualTo(123.45m));
}
}
While this is technically more of an integration example of C# unit testing with NUnit, it still runs fast and stays deterministic, which matters for CI pipelines.
Trends and good practices around NUnit in 2024–2025
If you look at active .NET projects on GitHub, you’ll see a mix of NUnit, xUnit, and MSTest. NUnit remains widely used, especially in older codebases and in teams that like its attribute‑driven style.
A few patterns show up repeatedly in the best examples of C# unit testing with NUnit examples today:
- Clear naming: Methods named like
MethodName_StateUnderTest_ExpectedResultor behavior‑focused names likeLoginAsync_ValidCredentials_ReturnsTrue. - Small tests: Each test verifies one behavior; multiple assertions are fine if they describe one logical outcome.
- Consistent Arrange–Act–Assert: Developers can skim tests quickly because the structure is predictable.
- CI integration: Tests run on every pull request using
dotnet testin GitHub Actions, Azure DevOps, or similar.
For general software quality and testing principles, resources like the Harvard CS50 course and testing discussions in academic settings (for example, MIT OpenCourseWare) provide helpful background, even if they don’t focus on NUnit specifically.
FAQ: examples of C# unit testing with NUnit examples
Q: Can you give another simple example of C# unit testing with NUnit for a string helper?
Yes. Here’s a short one that turns names into display labels:
public class NameFormatter
{
public string ToDisplayName(string firstName, string lastName)
{
if (string.IsNullOrWhiteSpace(firstName) || string.IsNullOrWhiteSpace(lastName))
throw new ArgumentException("First and last name are required");
return $"{firstName.Trim()} {lastName.Trim()}";
}
}
[TestFixture]
public class NameFormatterTests
{
[Test]
public void ToDisplayName_ValidNames_CombinesWithSpace()
{
var formatter = new NameFormatter();
var display = formatter.ToDisplayName("Ada", "Lovelace");
Assert.That(display, Is.EqualTo("Ada Lovelace"));
}
}
This is one of those examples of C# unit testing with NUnit examples you can write in under a minute but that saves you from embarrassing formatting bugs later.
Q: How do I organize many NUnit tests when examples include lots of fixtures?
Group tests by feature or domain concept instead of by layer. For instance, Billing.Tests, Auth.Tests, and Orders.Tests are easier to navigate than Services.Tests and Repositories.Tests. When a feature grows, split files by behavior (e.g., AuthService_Login_Tests.cs, AuthService_Registration_Tests.cs). This mirrors how most real examples of C# unit testing with NUnit are organized in active projects.
Q: Are there official references for NUnit syntax beyond these examples?
Yes. The NUnit documentation covers attributes, assertions, and runners in detail. For broader .NET testing practices, Microsoft’s official docs at learn.microsoft.com show how NUnit fits alongside xUnit and MSTest.
Q: Do these examples of C# unit testing with NUnit examples work with .NET 8 and .NET 9 previews?
Yes. NUnit supports modern .NET versions, and the patterns shown here—async tests, parameterized tests, mocking via interfaces—are all aligned with current .NET 8 and upcoming .NET 9 workflows. You mainly need the latest NUnit and NUnit3TestAdapter packages, plus a test runner compatible with your IDE or CI system.
These examples of C# unit testing with NUnit examples are meant to be copied, tweaked, and argued over in code reviews. If your tests read like these—clear intent, focused assertions, and modern C# style—you’re on the right track.
Related Topics
Practical examples of polymorphism in C++: function overloading
Best examples of dynamic memory allocation in C++: new and delete
Practical examples of C# variable declaration and initialization examples
Modern examples of C++ operator overloading: custom operators examples that actually matter
Best examples of C# conditional statements: examples & explanations
Real‑world examples of C# exception handling: 3 practical patterns every developer should know
Explore More C++ Code Snippets
Discover more examples and insights in this category.
View All C++ Code Snippets