
Unit tests are essential for software development. They help prevent errors that would otherwise be discovered late and difficult to locate. Visual Studio offers several extensions for C# developers to help us create unit tests.
In this blog post, I'll guide you through the most important steps in setting up a test project: Using an example, I'll create a simple unit test, provide some useful tips, and introduce helpful tools. The prerequisite for readable and effective unit tests is that the individual components of the code can be tested independently of the rest of the software.
You can learn how to make your code testable using interfaces in my next blog post, “Interfaces for better unit testing.”
Why should you perform unit tests?
- Ensuring the functionality of classes and methods.
- Simplifies the search for errors because certain sections of the code are covered by unit tests and can be excluded as a source of errors.
- After adjusting methods or classes, you can quickly and easily determine whether the functionality is still available.
Basics of unit tests
When creating unit tests, the following should be considered:
- To create meaningful unit tests, the individual components must be testable independently of each other. How to ensure the testability of your code with interfaces is explained using an example in the blog post "Interfaces for Better Unit Testing."
- A unit test always tests exactly one function.
- The input parameters and the desired result must be known in order to check the function for correctness.
- For each constraint of a function, a unit test must be created to cover all possible scenarios for which this function can be used.
Structure of a unit test
There are a few points to consider when creating structured and readable unit tests:
- A test method is written for each scenario.
- The name of the test method contains which method is being tested and what it is being tested for.
- The name of the test method ends with the word “Should”.
- Example: the test method for the function Calc(int n1, int n2) which adds two numbers could be named Calc::AddTwoNumbersShould()
- A test can usually be divided into three blocks
-
- Arrange:
– Preparation of the input parameters
– Determine the desired result
– Creating objects for testing - Act:
– Execution of the function to be tested
– Act usually consists of only a single line - Assert:
– Checking the results
– Check whether exceptions were thrown
- Arrange:
This division makes your tests more readable and understandable, because you can see at a glance what the input parameters and expected results are (Arrange block), what is being tested (Act block), and why the test fails (Assert block).
Example of unit testing a method for a calculator
The following steps are described
- Set up
- The class to be tested: Calculator
- How do you create a test project?
- How do you create a test class?
- Adding the reference to the project to be tested
- Creating a test
a. What can be tested? - Running a test
a. with the test explorer
b. with axocover
1. Setup
In the following example the following setup was used
- Visual Studio 2019
- NUnitTest: VisualStudio -> Tools -> NuGet package
NUnit v3.13.3
o NUnit3TestAdapter v.4.4. - Aoxcover: VisualStudio -> Tools -> Extensions and Updates
o AxoCover v 1.1.7.0
2. The class to be tested: Calculator
For this example, we will test the method Calc(int n1, int n2) of the Calculator class, which is shown in Figure 1. Here, we will limit ourselves to addition only.

3. Create a test project
Create a second project of type “NUnit 3 Unit Test Project” in the solution folder.

4. Create a test class
Now create a new test class in your new test project. Select the "NUnit Test Fixture" type and name it after the class or function you want to test. In the following, the name "CalcShould" was used.

In the newly created test fixture, a first test method TestMethod() is automatically created, as shown in Figure 4.

5. Adding the reference to the project to be tested
To access the classes from your actual project, you first need to add a reference to your project. To do this, right-click on the references of your test project and select "Add References." Then add your project.

Now you can add a using directive to your main project in your test fixture. You can now access the class you're testing.

6. Creating a test
Figure 7 shows a test method that tests the Calc method of the Calculator class. This method only tests the addition of two natural numbers.
Arrange:
In the Arrange block, an object of the Calculator class is created and the input parameters number1 and number2, as well as the expected result resultShould, are specified.
Act:
In the Act block, the Calc method is executed with the appropriate input parameters and the result is stored in the variable result.
Assert:
The Assert block checks whether the result match the expected result resultShould.

What can be tested in a unit test?
Below are some helpful ways to use the Assert class in unit tests.
| Using Assert | Meaning |
|---|---|
| Assert.That() Is.Equal(var x, var y) | Test can only be passed if x has the same value as y |
| Assert.Fail() | The test can only pass if the code fails before reaching this line. For example, because an unhandled exception is thrown beforehand. |
| Assert.That(x, Is.GreaterThan(y)); | The test can only be passed if x is greater than y |
Other ways to use Asser:
https://docs.nunit.org/articles/nunit/writing-tests/assertions/assertions.html
7. Run a unit test
Now that our test project is created and the first test is implemented, we need to run it. We can do this using Visual Studio's Test Explorer or the Axocover package.
Running tests with the Test Explorer
Visual Studio has its own Test Explorer for running tests. To use it, click Test -> Window -> Test Explorer. The Test Explorer will open and display all the tests in your solution. By clicking "Run All," you can run all your tests at once. Passing tests will be highlighted in green.

Run tests with Axocover
Like Test Explorer, Axocover also allows us to run single or multiple tests at once and then shows us the results

However, it also has a coverage function. Instead of running the tests normally, tests can also be "covered." The selected tests are then executed, and all lines of code that were run for the tests are highlighted in green to the right of the line numbering.
All lines that were not tested are highlighted in red. Figure 10 shows that all lines of the test were tested. More interesting for developers, however, is that Figure 11 shows that only the lines of our Calculator class under test that were tested are highlighted in green. Only the addition case was highlighted in green, and no other lines.


This feature is very useful for locating bugs in failed test runs or for verifying that tests cover all possible scenarios for a method. However, covering takes significantly longer than a normal test run and should therefore be used selectively.
Conclusion
NUnit allows us to create test projects and test fixtures to test individual classes or parts of a C# project. To test a function or class, one unit test should always be created per scenario, which should be divided into the three blocks Arrange, Act, and Assert.
In the Assert block, the Assert class can be used to check whether results match, whether exceptions were thrown, and much more. Finally, we demonstrated how to use Axocover and the Coverage function to check which lines of our code were executed for a test.
