Introduction to Unit Testing in C# for Beginners – Setup, Basics, and Helpful Tools

Alisa Schulz

20/06/2023


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
    1. Arrange:
      – Preparation of the input parameters
      – Determine the desired result
      – Creating objects for testing
    2. Act:
      – Execution of the function to be tested
      – Act usually consists of only a single line
    3. Assert:
      – Checking the results
      – Check whether exceptions were thrown

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

  1. Set up
  2. The class to be tested: Calculator
  3. How do you create a test project?
  4. How do you create a test class?
  5. Adding the reference to the project to be tested
  6. Creating a test
    a. What can be tested?
  7. 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.

Figure 1: Class to be tested

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

Figure 2: Creating a test project

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.

Figure 3: Creating a test class

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

Figure 4: Automatically created test class

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.

Figure 5: Adding the reference to the project to be tested

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

Figure 6: Adding the Using directive to the project to be tested in the test fixture

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.

Figure 7: Unit test for the Calc method for adding two integers

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.

Figure 8: Running the test with the Test Explorer

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

Figure 9: Running the test with AxoCover

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.

Figure 10: Coverage of the test method
Figure 11: Coverage of the tested method

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.


Written by Alisa Schulz


More articles

  • 26/11/2025
  • General, Hardware, Standards, Quality, Testing

Why EMC testing is vital in medical technology: Imagine a patient is lying in the hospital during critical monitoring. Suddenly, a visitor's smartphone rings – and the monitoring device... ...

Read more
  • 20/11/2025
  • General, Hardware, Quality, Technology, Testing

Have you ever considered sourcing inexpensive components from China? The temptation is strong, we know that. And we've already gained some experience, from which I... ...

Read more
  • 09/09/2025
  • General, Software

In previous blog posts, I have introduced two essential components of a simple and universally applicable software architecture: events with dispatchers, listeners, and data pools. These already allow for many simple use cases ...

Read more
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.

Strictly Necessary Cookies

Strictly Necessary Cookie should be enabled at all times so that we can save your preferences for cookie settings.