This is the second post in our Unit Testing in .NET Core series! In the previous post, we covered an introduction to unit testing and explored the various testing frameworks available in .NET Core. Now, in this post, we'll take a deep dive into xUnit.net and learn how to write our first unit test using this powerful library. So let's get started.
Setting up the project
To begin our journey into writing our first unit test, we'll create a straightforward calculator application. Using Visual Studio, we'll start by setting up a class library project named SimpleCalculatorApp. Inside this project, we'll add a class called Calculator.cs
, where we'll implement separate methods for Add, Subtract, Multiply, and Divide operations. Here's the resulting code section for the Calculator.cs
class.
namespace SimpleCalculatorApp
{
public class Calculator
{
public int Add(int num1, int num2)
{
return num1 + num2;
}
public int Subtract(int num1, int num2)
{
return num1 - num2;
}
public int Multiply(int num1, int num2)
{
return num1 * num2;
}
public int Divide(int num1, int num2)
{
if (num2 == 0)
{
throw new ArgumentException("Cannot divide by zero.");
}
return num1 / num2;
}
}
}
Setting up the test project
Now that we have our Calculator class and methods ready for testing, let's start writing the unit tests. To do this, we'll add a Unit Test project to our application by following these simple steps:
In Visual Studio, right-click on the existing solution that contains the SimpleCalculatorApp project, and choose "Add" from the context menu.
Select "New Project" to open the Create a new project dialog.
In the search box, type "xUnit" to find the xUnit.net Test Project template.
Choose the xUnit Test Project template, and give it a name like SimpleCalculatorApp.Tests, and click "Create."
Once the Unit Test project is added, we are ready to begin writing and running unit tests for the Calculator class. To do that, we need to add a reference to the SimpleCalculatorApp project in our test project. For that perform the following steps.
Right-click on the SimpleCalculatorApp.Tests project in the Solution Explorer.
Select "Edit Project File" from the context menu.
Add a reference to the SimpleCalculatorApp project by adding the following lines inside the
<ItemGroup>
element:<ItemGroup> <ProjectReference Include="..\SimpleCalculatorApp\SimpleCalculatorApp.csproj" /> </ItemGroup>
Create and Run xUnit Tests
In the SimpleCalculatorApp.Tests project, we will find a default test class named
UnitTest1.cs
.Rename
UnitTest1.cs
toCalculatorTests.cs
. A recommended practice is to name our test classes using the pattern<class-under-test>Tests.cs
.Inside the
CalculatorTests
class, let's write our first xUnit test method. In this test method, we will test theAdd
method from theCalculator
class.using SimpleCalculatorApp; using Xunit; public class CalculatorTests { [Fact] public void Add_ShouldReturnCorrectSum() { // Arrange var calculator = new Calculator(); int a = 5; int b = 3; // Act int result = calculator.Add(a, b); // Assert Assert.Equal(8, result); } }
Let's break down the above code step by step.
The code starts by importing the Xunit namespace, which provides the necessary classes and attributes to write unit tests.
The
CalculatorTests
class is created, which will contain our unit test methods. It is marked with the[Fact]
attribute, indicating that the method inside this class is a test that should be executed. The[Fact]
is an attribute provided by xUnit to define a method as a unit test method. The test runners will take up all the methods decorated by[Fact]
attribute for execution.This is followed by the test method itself. The test method is called
Add_ShouldReturnCorrectSum
. This method tests theAdd
method of a class calledCalculator
. The test method is divided into three parts - Arrange, Act and Assert.The
Arrange
section is where we set up the test environment. We create an instance of theCalculator
class and assign values to two variables,a
andb
, representing positive numbers (in this case, 5 and 3).The
Act
section is where we perform the action we want to test. Here, we call theAdd
method of theCalculator
class and pass the values ofa
andb
as arguments. The result of the addition is stored in a variable calledresult
.The
Assert
section is where we verify whether the test passed or failed. We use theAssert.Equal
method from Xunit to check if theresult
of the addition is equal to the expected value, which is 8 (5 + 3 = 8).
Running the Test
Now that we have written our first unit test, we need to run the tests. For running the test we need to open the Test Explorer in Visual Studio. For this, choose Test on the Visual Studio menu, choose Windows, and then choose Test Explorer (or press Ctrl + E, T). When the Test Explorer window is loaded we can see our Add_ShouldReturnCorrectSum
test in the window.
Right-click on the test and choose the Run option. The test runner will execute the test and we can see the results in the test runner window. As our test is passed we can see a green checkmark next to the test. If it had failed we shall see a red X symbol next to the test and additional information about the failure.
Let's now explore how we can test exceptions using the Divide
method as an example.
Unit Testing Exceptions
It's essential to ensure that our code handles exceptional scenarios gracefully, such as preventing division by zero when performing mathematical operations. Let's take a look at how we can test the Divide
method to verify its exception-handling behavior.
To write a unit test for the exception scenario, we'll use xUnit.net's Assert.Throws
method. Here's the test code:
[Fact]
public void Divide_ShouldThrowArgumentException_WhenDividingByZero()
{
// Arrange
Calculator calculator = new Calculator();
int numerator = 10;
int denominator = 0;
// Act and Assert
Assert.Throws<ArgumentException>(() => calculator.Divide(numerator, denominator));
}
In the test method Divide_ShouldThrowArgumentException_WhenDividingByZero
, we first arrange the necessary setup by creating an instance of the Calculator
class. Next, we set up the input values, where numerator
can be any valid number, but denominator
is intentionally set to zero to invoke the exception scenario.
The core of this test is the line Assert.Throws<ArgumentException>(() => calculator.Divide(numerator, denominator));
. This line instructs xUnit to execute the Divide
method with the provided arguments and verify that it indeed throws an ArgumentException
. If the exception is thrown as expected, the test will pass successfully, indicating that our code correctly handles the exceptional scenario of division by zero.
If we run the test we can see that the test indeed is passing.
Add the remaining tests
To ensure our code works correctly, we'll add unit tests for the remaining methods: Subtract
, Multiply
, and the non-zero denominator scenario of Divide
. Once these tests are added, our test class will be complete.
using System;
using SimpleCalculatorApp;
using Xunit;
namespace SimpleCalculatorApp.Tests
{
public class CalculatorTests
{
[Fact]
public void Add_ShouldReturnCorrectSum()
{
// Arrange
Calculator calculator = new Calculator();
int num1 = 5;
int num2 = 10;
// Act
int result = calculator.Add(num1, num2);
// Assert
Assert.Equal(15, result);
}
[Fact]
public void Subtract_ShouldReturnCorrectDifference()
{
// Arrange
Calculator calculator = new Calculator();
int num1 = 15;
int num2 = 7;
// Act
int result = calculator.Subtract(num1, num2);
// Assert
Assert.Equal(8, result);
}
[Fact]
public void Multiply_ShouldReturnCorrectProduct()
{
// Arrange
Calculator calculator = new Calculator();
int num1 = 3;
int num2 = 6;
// Act
int result = calculator.Multiply(num1, num2);
// Assert
Assert.Equal(18, result);
}
[Fact]
public void Divide_ShouldReturnCorrectQuotient()
{
// Arrange
Calculator calculator = new Calculator();
int num1 = 20;
int num2 = 4;
// Act
int result = calculator.Divide(num1, num2);
// Assert
Assert.Equal(5, result);
}
[Fact]
public void Divide_ShouldThrowArgumentException_WhenDividingByZero()
{
// Arrange
Calculator calculator = new Calculator();
int num1 = 10;
int num2 = 0;
// Act & Assert
Assert.Throws<ArgumentException>(() => calculator.Divide(num1, num2));
}
}
}
Run all the tests. We can see that all the tests are indeed passing.
Summary
In this blog post, we delved into the world of setting up and writing unit tests using xUnit.net. Taking a simple calculator application as an example, we crafted unit tests for various methods. The journey included understanding the significance of the [Fact]
attribute, mastering the Arrange-Act-Assert pattern, and effectively handling exception scenarios through unit testing.
Excitingly, there's more to explore in xUnit.net! In the upcoming posts, we'll explore additional features in xUnit.net that can enhance the maintainability and robustness of our tests.