C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
98: Unit Testing with NUnit
By now, you've probably written enough code to realize that manually running your app and clicking through menus to see if a specific edge case works is a nightmare. That's where NUnit comes in. It's one of the most popular testing frameworks in the .NET ecosystem, and once you get the hang of it, you'll feel significantly more confident hitting "deploy."
How do I actually get an NUnit test project running?
First off, you don't put your tests in your main project. You create a separate project in your solution—usually named something like YourProject.Tests. You'll need a few NuGet packages to make this work: nunit (the framework), NUnit3TestAdapter (so Visual Studio/JetBrains can actually "see" the tests), and Microsoft.NET.Test.Sdk.
Once those are in, you just mark your methods with the [Test] attribute. That's it. The test runner looks for that attribute and knows it needs to execute that specific method. Here is a simple starting point using a DiscountCalculator class we're pretending exists in your main project:
using NUnit.Framework;
[TestFixture]
public class DiscountCalculatorTests
{
[Test]
public void ApplyDiscount_ShouldReducePrice()
{
// This is a basic test
var calculator = new DiscountCalculator();
var result = calculator.ApplyDiscount(100m, 0.2m); // 20% off 100
Assert.That(result, Is.EqualTo(80m));
}
}
What's the difference between the "Classic" and "Constraint" Assert styles?
If you look at old tutorials, you'll see Assert.AreEqual(expected, actual). That's the "Classic" style. NUnit also has a "Constraint" model, which is Assert.That(actual, Is.EqualTo(expected)).
I strongly recommend using the Constraint model. Why? Because it reads like a sentence. When you're dealing with more complex assertions—like checking if a string starts with a certain letter or if a collection contains a specific item—the constraint model is much more flexible and intuitive. For example, Assert.That(result, Is.GreaterThan(0)) is way more natural than trying to find a specific "AssertGreaterThan" method.
How do I test ten different scenarios without writing ten different methods?
Writing a new method for every single single input value is a waste of your time. This is where [TestCase] is a lifesaver. It allows you to parameterize your tests. You define the inputs and the expected output right in the attribute, and NUnit runs the test method once for every case you provide.
Let's apply this to our DiscountCalculator to test a few different discount tiers:
[TestCase(100, 0.1, 90)] // 10% off 100 = 90
[TestCase(100, 0.5, 50)] // 50% off 100 = 50
[TestCase(0, 0.1, 0)] // 10% off 0 = 0
[TestCase(100, 0, 100)] // 0% off 100 = 100
public void ApplyDiscount_VariousInputs_ReturnsExpectedPrice(decimal price, decimal discount, decimal expected)
{
var calculator = new DiscountCalculator();
var result = calculator.ApplyDiscount(price, discount);
Assert.That(result, Is.EqualTo(expected));
}
What exactly is "Arrange, Act, Assert" and why does everyone talk about it?
You'll hear the "AAA" pattern constantly. It's not a hard rule enforced by the compiler, but it's a discipline that keeps your tests from becoming a jumbled mess. If your tests are disorganized, you'll spend more time debugging the test than debugging the actual code.
- Arrange: Set up your objects. Initialize the class you're testing and prepare the input data.
- Act: Call the actual method you're testing. This should usually be a single line of code.
- Assert: Verify that the outcome is what you expected.
I usually leave a blank line between these three sections. It makes the test instantly scannable for anyone else (including future you) who has to read it six months from now.
[Test]
public void CalculateTax_ValidAmount_ReturnsCorrectTax()
{
// Arrange
var taxCalc = new TaxCalculator(0.05m);
var amount = 100m;
// Act
var result = taxCalc.GetTax(amount);
// Assert
Assert.That(result, Is.EqualTo(5m));
}
📋 Practical Task
Exercise: Building a Loyalty Point Validator Test Suite
You are tasked with testing a new LoyaltyProgram class. The business logic is as follows: users earn 1 point for every $10 spent, but if they are a "Gold" member, they earn 2 points for every $10 spent. Spending less than $10 earns 0 points.
Your Task:
- Create a
LoyaltyProgramclass with a methodCalculatePoints(decimal spend, bool isGoldMember)that implements the logic above. - Create an NUnit test project and write a test class named
LoyaltyProgramTests. - Implement a
[TestCase]method that verifies at least five different scenarios:- A standard member spending $50 (Should be 5 points).
- A Gold member spending $50 (Should be 10 points).
- A standard member spending $5 (Should be 0 points).
- A Gold member spending $5 (Should be 0 points).
- A standard member spending $105 (Should be 10 points - assume floor rounding).
- Ensure your test method follows the Arrange, Act, Assert pattern.
There are no comments for now.