Unit testing in C#

๐Ÿงช Unit Testing for .NET Developers: Boost Your Skills and Squash Bugs

Are you tired of shipping bugs to production? Want to feel confident when changing legacy code? Unit testing is your secret weapon. This guide walks you through the basics with real examples in .NET using XUnit, so you can write reliable code faster.

๐Ÿง  What is Unit Testing and Why Should You Care?

Unit testing is about testing small parts of your code in isolation. Think of it like testing an engine one part at a time.

✅ Benefits of Unit Testing

  • Prevents bugs early
  • Improves code quality
  • Acts as documentation
  • Supports refactoring safely

Example: A math error caught by a test

[Fact] public void Divide_ByZero_ShouldThrowException() { var calculator = new Calculator(); Assert.Throws<DivideByZeroException>(() => calculator.Divide(10, 0)); }

๐Ÿ› ️ Setting Up Your .NET Unit Testing Environment

  1. Install Visual Studio

  2. Create a new Test Project: DemoLibrary.Tests

  3. Add NuGet packages:

    • xunit
    • xunit.runner.visualstudio

๐Ÿค– Writing Your First Unit Tests: A Practical Example

Suppose you have a simple calculator:

Calculator.cs

public class Calculator { public int Add(int a, int b) => a + b; public int Divide(int a, int b) => a / b; }

CalculatorTests.cs

public class CalculatorTests { [Fact] public void Add_SimpleValuesShouldCalculate() { // Arrange var calc = new Calculator(); // Act var result = calc.Add(2, 2); // Assert Assert.Equal(4, result); } }


๐Ÿ“Š Using Theories and InlineData for Parameterized Tests

public class CalculatorTheoryTests { [Theory] [InlineData(2, 2, 4)] [InlineData(0, 0, 0)] [InlineData(-1, 1, 0)] public void Add_VariousInputs_ShouldReturnExpectedResult(int a, int b, int expected) { var calc = new Calculator(); var result = calc.Add(a, b); Assert.Equal(expected, result); } }

๐Ÿšง Handling Edge Cases

[Fact] public void Add_MaxIntValues_ShouldOverflow() { var calc = new Calculator(); Assert.Throws<OverflowException>(() => { checked { calc.Add(int.MaxValue, 1); } }); }

⚠️ Testing for Exceptions

[Fact] public void Divide_ByZero_ShouldThrowDivideByZeroException() { var calc = new Calculator(); var ex = Assert.Throws<DivideByZeroException>(() => calc.Divide(10, 0)); Assert.Equal("Attempted to divide by zero.", ex.Message); }

๐Ÿงฑ Testing Complex Code: Data Access Example

PersonRepository.cs

public class PersonRepository { private readonly IFileService _fileService; public PersonRepository(IFileService fileService) { _fileService = fileService; } public void SavePerson(string name) { var line = $"Name: {name}"; _fileService.WriteLine("people.txt", line); } }

IFileService.cs

public interface IFileService { void WriteLine(string fileName, string line); }

Test using Moq

[Fact] public void SavePerson_ShouldCallWriteLineWithCorrectArguments() { var mockFileService = new Mock<IFileService>(); var repo = new PersonRepository(mockFileService.Object); repo.SavePerson("Ankit"); mockFileService.Verify(fs => fs.WriteLine("people.txt", "Name: Ankit"), Times.Once); }

๐Ÿ“ Refactoring for Testability: SRP Example

Instead of this:

public void AddNewPerson() { // Fetch, transform, save — all in one! }

Do this:

public void AddPersonToList() { /* Logic */ } public void ConvertToCSV() { /* Logic */ }

Each method is now testable in isolation.


✅ Key Takeaways

  • Unit testing = testing small, independent chunks of code.
  • Use xUnit with Arrange-Act-Assert for clarity.
  • Test edge cases and exceptions.
  • Refactor to apply Single Responsibility Principle (SRP).
  • Mock dependencies to test external interactions.

Post a Comment

0 Comments