iTranslated by AI
How to Implement Unit Testing with C# and xUnit in Visual Studio 2022
Introduction
When developing applications in C#, you may want to verify whether functions or processes are working correctly.
However, manually checking outputs with Console.WriteLine() every time is time-consuming, and it's easy to overlook bugs.
In this article, I will explain how to easily automate testing on Visual Studio 2022 using the C# unit testing framework xUnit.
- For those new to testing in C#
- For those who want to use xUnit but don't know how to use it
- For those who want to check the operational procedures in Visual Studio as well
This content is intended for such people.
Since I personally struggled with the GUI operations when I first introduced xUnit, I've tried to explain it carefully with many screenshots.
The code used in this explanation is available here:
Preparing the App Project
Project Creation
For this demonstration, I created a project using the Console App (.NET Framework) template.
Preparing the Code
We will create a Calculator class and use it in the Program class.
using System;
namespace MyConsoleApp
{
class Program
{
static void Main()
{
Calculator myCalculator = new Calculator();
int result = myCalculator.Add(1, 2);
Console.WriteLine(result);
}
}
}
using System;
namespace MyConsoleApp
{
public class Calculator
{
public int Add(int input1, int input2)
{
return input1 + input2;
}
}
}
Automating Testing with xUnit
How should we verify that the Add() function in the Calculator class we created works correctly?
Changing the arguments of Add() one by one to confirm the results is tedious...
Therefore, we will automate the testing process with xUnit.
Creating a Test Project
Select File > New > Project.

Search for "xUnit" and select xUnit Test Project.

Change the Solution field to Add to solution.
Click Next, select the framework according to the .NET version you are using for development, and click Create.

Adding a Reference to the App Project
Configure the settings so that the Calculator class can be referenced from the test project.
Right-click on Solution Explorer > Test Project Name > References.
Select Add Reference....

Click Projects > Solution at the top left of the screen.
Check the box next to your own app project and click OK to close.

Creating Test Code
Testing methods in xUnit can be broadly classified into the following two types:
- Use
[Fact]if you just want to see if it works/doesn't work - Use
[Theory]if the output changes depending on the input
| Attribute | Main Features |
|---|---|
[Fact] |
Basic test without arguments. Suitable for fixed tests that run under the same conditions every time. |
[Theory] |
Parameterized test with arguments. Allows verifying the same process with multiple inputs. |
In [Theory], you specify the input and expected output as a set, such as [InlineData(1, 2, 3)].
| Attribute | Best Use Case | Main Features / Decision Points |
|---|---|---|
[InlineData] |
When you want to directly write a small amount of simple data | Concise and written right where it's visible. Ideal for small datasets. |
[MemberData] |
When you want to reuse slightly complex data or multiple combinations | Sufficient if you can statically return arrays, lists, etc. |
[ClassData] |
When complex logic, external data, or dynamic generation is required | Allows for flexible processing with a custom class. Best for large or dynamic datasets. |
Here is a code example.
using Xunit;
using MyConsoleApp;
using System.Collections;
using System.Collections.Generic;
namespace MyTest
{
public class CalculatorTest
{
// Instantiate the class to be tested from MyConsoleApp
private readonly Calculator _calculator = new();
[Fact]
public void AddTestFact()
{
Assert.Equal(3, _calculator.Add(1, 2));
}
// Pattern using InlineData
[Theory]
[InlineData(1, 2, 3)]
[InlineData(2, 3, 5)]
public void AddTestTheory(int input1, int input2, int expected)
{
int actual = _calculator.Add(input1, input2);
Assert.Equal(expected, actual);
}
// Pattern using MemberData
[Theory]
[MemberData(nameof(AddTestMemberData))]
public void AddTestMember(int a, int b, int expected)
{
Assert.Equal(expected, _calculator.Add(a, b));
}
public static IEnumerable<object[]> AddTestMemberData =>
new List<object[]>
{
new object[] { 1, 2, 3 },
new object[] { 5, 7, 12 }
};
// Pattern using ClassData
[Theory]
[ClassData(typeof(AddTestData))]
public void AddTestClassData(int a, int b, int expected)
{
int result = _calculator.Add(a, b);
Assert.Equal(expected, result);
}
}
// Test data class for ClassData
public class AddTestData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { 10, 20, 30 };
yield return new object[] { -5, 5, 0 };
yield return new object[] { 100, 200, 300 };
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
Running Tests
Click Test > Test Explorer.

Clicking the arrow button at the top left of the Test Explorer screen will execute all tests.

If a test fails, it will be marked with a red X.
By selecting the corresponding row, you can check the actual values. In this case, you can see that while the expected output was 4, the actual output was 3.

Useful Tips
Specifying Test Names
Test results display the function names by default.
By using DisplayName, you can display any name you like.

using Xunit;
using MyConsoleApp;
using System.Collections;
using System.Collections.Generic;
namespace MyTest
{
public class CalculatorTest
{
// Instantiate the class to be tested from MyConsoleApp
private readonly Calculator _calculator = new();
[Fact(DisplayName = "You can display any name you like")]
public void AddTestDisplayName()
{
Assert.Equal(3, _calculator.Add(1, 2));
}
}
}
Outputting Logs
If you want to check logs during test execution, Console.WriteLine will not display them correctly.
By using ITestOutputHelper, you will be able to check the logs.

using Xunit;
using MyConsoleApp;
using System.Collections;
using System.Collections.Generic;
namespace MyTest
{
public class CalculatorTest
{
// Instantiate the class to be tested from MyConsoleApp
private readonly Calculator _calculator = new();
private readonly ITestOutputHelper _output;
public CalculatorTest(ITestOutputHelper output)
{
_output = output;
}
[Fact]
public void AddTestLog()
{
Assert.Equal(3, _calculator.Add(1, 2));
_output.WriteLine("You can display logs");
Console.WriteLine("This one will not be displayed");
}
}
}
Conclusion
So far, we have introduced the steps for implementing unit tests for C# functions using xUnit, along with the screen operations in Visual Studio 2022.
By automating tests, you can establish an environment for early bug detection and refactoring with peace of mind.
The next step is to introduce CI (Continuous Integration).
By setting up a system where tests are automatically executed at the time of a Push or Pull Request, even more efficient development becomes possible.
Discussion