Overview
The xUnit testing framework is a popular choice in .NET 8 for writing and executing unit tests in C# and is an integral part of software development. We will cover essential concepts and provide code examples in this comprehensive guide as we explore the fundamentals of unit testing with xUnit.
Prerequisites
To follow this article, ensure we have the following installed.
- .NET SDK 8.0 or later (Download .NET SDK)
- Visual Studio Code or Visual Studio (optional but recommended)
Creating a Sample Project
The first step is to create a simple C# console application and its corresponding unit test project using xUnit.
# Create a new console application
dotnet new console -n ZiggyRafiqConsoleApp
# Create a new xUnit test project
dotnet new xunit -n ZiggyRafiqConsoleApp.Tests
Write Our Console App
For the Program.cs we just printed my name Ziggy Rafiq and this article title. As the code example below shows us.
Console.WriteLine("Hi and Welcome to Ziggy Rafiq Article on Unit Testing with xUnit in .NET 8: A Comprehensive Guide");
Create Our Calculator Class
We just create a simple Calculator class with simple functions such as Add, Subtract, Multiply, Divide, and the key one Dispose of as the code example below.
namespace ZiggyRafiqConsoleApp
{
public class Calculator : IDisposable
{
// Add method
public int Add(int a, int b)
{
return a + b;
}
// Subtract method
public int Subtract(int a, int b)
{
return a - b;
}
// Multiply method
public int Multiply(int a, int b)
{
return a * b;
}
// Divide method
public double Divide(int a, int b)
{
if (b == 0)
{
throw new ArgumentException("Cannot divide by zero.");
}
return (double)a / b;
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Dispose of managed resources here go here
}
// Dispose of unmanaged resources goes here , if any
disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~Calculator()
{
Dispose(false);
}
}
}
Data Source
We will create two files for our Data Source one is an interface and the other is a class as code example below.
namespace ZiggyRafiqConsoleApp
{
public interface IDataSource
{
string GetData();
}
}
namespace ZiggyRafiqConsoleApp
{
public class DataService
{
private readonly IDataSource _dataSource;
public DataService(IDataSource dataSource)
{
_dataSource = dataSource;
}
public string ProcessData()
{
return _dataSource.GetData().ToUpper();
}
}
}
Now we are all set to create our Unit Tests 😊
Writing Our First Test
Our preferred code editor should open the ZiggyRafiqConsoleApp.Tests project. XUnit automatically creates a test file named UnitTest1.cs.
To replace UnitTest1.cs with the following code example, please refer to the below code example. As an example, we will create a simple test method named TestAddition. The [Fact] attribute indicates that this method is a test.
namespace ZiggyRafiqConsoleApp.Tests
{
public class ZiggyRafiqConsoleAppTests
{
[Fact]
public void TestAddition()
{
// Arrange
int a = 2;
int b = 3;
// Act
int result = a + b;
// Assert
Assert.Equal(5, result);
}
}
}
Arrange
Setting up the necessary objects and conditions.
Act
Performing the action or invoking the method under test.
Assert
Verifying that the result is as expected.
Running the Tests
Now, let's run our unit tests.
# Navigate to the test project directory
cd ZiggyRafiqConsoleApp.Tests
# Run the tests
dotnet test
# Navigate to the test project directory
cd ZiggyRafiqConsoleApp.Tests
# Run the tests
dotnet test
There should be an output indicating that one test has been run and passed.
Writing More Tests
Let's add a few more tests to cover different scenarios. Let's replace UnitTest1.cs with the following code example. We will add a subtraction test and a parameterized multiplication test using the [Theory] and [InlineData] attributes.
namespace ZiggyRafiqConsoleApp.Tests
{
public class ZiggyRafiqConsoleAppTests
{
[Fact]
public void TestSubtraction()
{
// Arrange
int a = 5;
int b = 3;
// Act
int result = a - b;
// Assert
Assert.Equal(2, result);
}
[Theory]
[InlineData(2, 3, 6)]
[InlineData(5, 4, 20)]
[InlineData(0, 7, 0)]
public void TestMultiplication(int a, int b, int expected)
{
// Act
int result = a * b;
// Assert
Assert.Equal(expected, result);
}
}
}
Advanced Concepts
Test Fixture
Generally, a test fixture contains one or more test methods. It can be used to share setup and cleanup code between tests.
namespace ZiggyRafiqConsoleApp
{
public class CalculatorFixture : IDisposable
{
public Calculator Calculator { get; private set; }
public CalculatorFixture()
{
// Initialize resources, create instances, etc.
Calculator = new Calculator();
}
public void Dispose()
{
// Clean up resources, dispose of instances, etc.
Calculator.Dispose();
}
}
}
namespace ZiggyRafiqConsoleApp.Tests
{
public class CalculatorTests : IClassFixture<CalculatorFixture>
{
private readonly CalculatorFixture _fixture;
public CalculatorTests(CalculatorFixture fixture)
{
_fixture = fixture;
}
[Fact]
public void TestAddition()
{
// Arrange
Calculator calculator = _fixture.Calculator;
int a = 5;
int b = 10;
// Act
int result = calculator.Add(a, b);
// Assert
Assert.Equal(15, result);
}
}
}
Mocking
The use of mocking libraries, such as Moq, can help isolate code units for testing.
using Moq;
namespace ZiggyRafiqConsoleApp.Tests;
public class DataServiceTests
{
[Fact]
public void TestDataProcessing()
{
// Arrange
var mockDataSource = new Mock<IDataSource>();
mockDataSource.Setup(d => d.GetData()).Returns("HELLO, I AM ZIGGY RAFIQ");
var dataService = new DataService(mockDataSource.Object);
// Act
string result = dataService.ProcessData();
// Assert
Assert.Equal("HELLO, I AM ZIGGY RAFIQ", result);
}
}
Summary
A hands-on introduction to unit testing using xUnit in .NET 8 is presented in this guide. We cover writing and running tests, understanding how tests work, and exploring advanced concepts like test fixtures and mocking. Building reliable, maintainable software requires effective unit testing. We need to embrace unit testing as an integral part of our development process as we continue to develop our projects to ensure robustness and correctness.
Please do not forget to like this article if you have found it useful and follow me on my LinkedIn https://www.linkedin.com/in/ziggyrafiq/ also I have uploaded the source code for this article on my GitHub Repo https://github.com/ziggyrafiq/dotnet8-xunit-unit-testing-guide
Happy testing!