Unit Testing in C#: A Surprisingly Simple Way to Write Better Apps
Unit Testing in C#: A Surprisingly Simple Way to Write Better Apps

Unit Testing in C#: A Surprisingly Simple Way to Write Better Apps

Question – What do:

  • Dapper (13.4k ⭐)
  • Newtonsoft.JSON (8.4k ⭐)
  • AutoMapper (7.9k ⭐)
  • Autofac (3.4k GitHub stars ⭐)

…all have in common?

They are all free open-sourced libraries, they are used by millions of C# developers around the world, and there are hundreds of unit tests written for each of them.

If I have seen further it is by standing on the shoulders of Giants.

Isaac Newton

Just like our buddy Isaac stood on the shoulders of giants, aka used the help of others to help him progress further and faster, we also rely on the libraries mentioned above to develop our apps in the shortest amount of time.

Since so many developers rely on them, they have to be stable and have as few bugs as possible. And they use unit tests to achieve that goal.

One of the easiest ways to minimize the number of bugs in any app is with unit testing.

What is unit testing?

Unit testing is a type of testing you test small, isolated pieces of your code.

The purpose of unit testing is to confirm that each small piece, that is unit, works as expected on its own. A unit is usually a single method in a single class, but it can be the whole class itself.

Here’s the deal. If you want to write better apps then keep reading…

Unit tests have many benefits:

  • It increases confidence in changing code. If good unit tests are written and if they are run every time any code is changed, we will be able to quickly catch any new bug introduced with the latest change.
  • Development is faster. How? Without unit tests, we usually have to check that the code works through the app UI. With unit testing in place, we write the test, write the code and run the test. Writing tests takes time but the time is compensated by the less amount of time it takes to run the tests.
  • The cost of fixing a bug detected during unit testing is smaller than the cost of fixing the same bug later. It’s less expensive to fix a bug during development, than later when your changes are already deployed to production.
  • Debugging is easy. When a test fails, only the latest changes need to be debugged.

Will unit tests magically solve all your problems?

No. Unit testing is not a silver bullet.

Unit tests only test the functionality of the small, independent pieces of your code. They will not catch integration errors, such as features that need multiple units to be executed to work properly. That’s why you should also perform integration testing and UI testing.

The other thing is that developers need to invest (yes, invest, since that time is repaid in the long term) extra time to write and maintain unit tests.

This is not an easy task, sometimes. We all have deadlines to meet, code reviews to perform, meetings to attend.

Are you lonely?
Tired of working on your own? Do you hate making decisions?
Hold a meeting! You can see people, show charts, feel important…
All on company time!

Here’s how to get started with unit testing in C#

How do we test our code? A unit test usually consists of three main actions:

  1. Arrange objects, creating and setting them up as necessary.
  2. Act on an object.
  3. Assert that something is as expected.

Let’s go through one example to see these main parts in a single unit test. We have a simple Calculator class, which has one method AddTwoNumbers.

public class Calculator
{
    public int AddTwoNumbers(int first, int second)
    {
        return first + second;
    }
}

(At this point you are probably wondering why I’m showing you this simple example since the production code is never that simple. Maybe your codebase is a beast called legacy code. But I’m showing you how to test this simple code so we can fully focus on the mechanics of writing unit tests, and put the actual code in the second place.
Plus, you never know, maybe will have to test the exact same function in the future. In that case, lucky you…
)

A simple test to check that AddTwoNumbers returns the sum of numbers passed in as parameters might look like:

[Fact]
public void AddTwoNumbers_returns_sum_of_numbers()
{
    //Arrange
    var calculator = new Calculator();
    //Act
    var result = calculator.AddTwoNumbers(5, 6);
    //Assert
    Assert.Equal(11, result);
}
  1. In the Arrange part of the unit test, we initialize the class we want to test.
  2. Next, in the Act part of the unit test, we call the method which is being tested.
  3. In the Assert part of the unit test, we check that the result is as expected. We use Assert.Equal method to do that. That method is a part of a xUnit framework, which provides us with the methods to make writing tests easier and faster.

How to name tests? Use UnitOfWork_ExpectedBehaviour_ScenarioUnderTest naming convention.

UnitOfWork name could be as simple as a method name (if that’s the whole unit of work) or more abstract if it’s a use case that encompasses multiple methods or classes such as UserLogin or RemoveUser.

ExpectedBehaviour is the result we expect to occur after executing the unit of work.

ScenarioUnderTest represents a scenario or circumstances under which the unit of work is being executed.

How to add tests to the existing project

Ok, the previous example was a simple way to show the elements of a unit test. Next, let’s go a bit further and see how to test more advanced methods.

In order to add a test project to your existing solution, right-click on the Solution, Add -> New Project…

Then, select the testing framework project template.

As you can see, there are different options available: xUnit, NUnit or MSTest. Which one testing framework to choose?

They are all pretty similar, but I prefer xUnit.

After you create a unit testing project, make sure to add reference to the project you would like to test. That project will be a test target.

To the Moon and back

We create a class called Rocket, which we will test:

public class Rocket
{
    public int Speed { get; set; } = 0;
    public bool EnginesAreWorking { get; set; }
    public string Direction { get; set; } = "No direction...";

    public void FlyToTheMoon()
    {
        Speed = 29000;
        EnginesAreWorking = true;
        Direction = "To the Moon!";
    }
}

Please note that the Rocket class has to be declared as public, in order to be visible in the testing project.

Now we create a unit test to check that, when we launch the rocket to the Moon, that it reaches the desired speed, engines are working and the direction is correct. Create a new class and name it RocketTests. This class will be a test file that will contain all the test code for the Rocket class.

The naming convention is ClassName + Tests(ending with plural Tests, since every test class usually contains more than one test).

The test for the FlyToTheMoon method looks like:

public class RocketTests
{
    [Fact] // In xUnit, the method has to be public and have Fact attribute.
    public void FlyToTheMoon_goes_to_the_moon()
    {
        var rocket = new Rocket();

        rocket.FlyToTheMoon();

        Assert.True(rocket.EnginesAreWorking);
        Assert.Equal(29000, rocket.Speed);
        Assert.Equal("To the Moon!", rocket.Direction);
    }
}

In xUnit, the method has to be public and have the Fact attribute, and the class has to be public, in order to recognize tests.

In the Arrange step of the test, we initialize the Rocket class, then we execute the FlyToTheMoon method in the Act step. Finally, in the Assert step, we check that the properties Speed, EnginesAreWorking and Direction are set to the expected values.

How to run unit tests in Visual Studio?

You can run tests by:

  • Hitting the Ctrl+R,A keyboard shortcut
  • Through the Test Explorer window, go to the Test menu bar and select the Test Explorer window. Test Explorer window is really useful if you would like to filter your test suite, aka your collection of tests, by some criteria. You could show only failed tests or only tests that are covering one module of the system.
  • Or right-click on the test or test class you want to run, and select Run Test(s)

What this will do, is to invoke a test runner. A test runner is a tool that is a part of the test framework. It will scan for all test methods and execute them one by one, within the same test run.

Conclusion

Testing is a very important part of every application project. It makes sure the application behaves as expected, minimizes the number of bugs, and improves our confidence while refactoring.

That’s why it should be part of every developer’s toolbox.