Get up to 80 % extra points for free! More info:

Lesson 2 - Testing in C# .NET - Introduction to unit tests

In the previous lesson, Introduction to software testing in C# .NET, we made a relatively solid introduction to the issue. We also introduced the v-model that illustrates the relationship between the individual design phase outputs and the relevant tests.

We always write tests based on design, not the implementation. In other words, we create them based on the expected functionality. It can be obtained either directly from the customer (and that's in the case of acceptance tests) or from the programmer (architect), where he specifies how different methods should behave. Today, we're going to focus on these tests, that are called unit tests, and which test the detailed specification of the application, thus its classes.

Remember that we never write tests according to how something is programmed inside! It could very easily seduce our thinking in just one way, and we could forget that the method might get some other inputs which it isn't expecting. In fact, testing isn't related with implementation at all, we always test whether the requirements are met.

Which classes to test

Unit tests test individual methods in classes. For sure, I repeat that it doesn't make much sense to test single-purpose methods, for example, in models that just pick something from the database. To be more specific, there is no point in testing a method like this:

public void InsertItem(string name, double price)
{
    using (var db = new DatabaseEntities())
    {
        db.items.Add(new Item(name, price));
        db.SaveChanges();
    }
}

The method inserts an item to the database. Typically, it is used only in a form, and if it does not work, acceptance tests will find it out as the new item would not appear on the list. There are lots of similar methods in the application and we would unnecessarily waste time by covering something that we can easily cover in other tests.

Unit tests are most often found with libraries, that is, the tools that the programmer uses in more places or even in multiple projects and should be 100% functional. You may remember when you used a library downloaded, for example, from GitHub. Most likely, there were also tests included. For example, if we write an application in which we often need some mathematical calculations, such as factorials and other probability functions, we create a library, for sure, for these calculations, and it's a good idea to cover such library with tests.

Example

As you probably expect, we'll create such a class and test it. In order to save time, let's just create a simple calculator which will be able to:

  • add
  • subtract
  • multiply
  • divide

Creating a project

In practice, there would be more complicated calculations in the class, but we won't deal with it here. Create a new project, a console application named CalculatorApp. Add a public class called Calculator with the following implementation:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CalculatorApp
{
    /// <summary>
    /// Represents a simple calculator
    /// </summary>
    public class Calculator
    {

        /// <summary>
        /// Adds 2 numbers
        /// </summary>
        /// <param name="a">First number</param>
        /// <param name="b">Second number</param>
        /// <returns>Sum of 2 numbers</returns>
        public double Add(double a, double b)
        {
            return a + b;
        }

        /// <summary>
        /// Subtracts 2 numbers
        /// </summary>
        /// <param name="a">First number</param>
        /// <param name="b">Second number</param>
        /// <returns>Difference of 2 numbers</returns>
        public double Subtract(double a, double b)
        {
            return a - b;
        }

        /// <summary>
        /// Multiplies 2 numbers
        /// </summary>
        /// <param name="a">First number</param>
        /// <param name="b">Second number</param>
        /// <returns>Product of 2 numbers</returns>
        public double Multiply(double a, double b)
        {
            return a * b;
        }

        /// <summary>
        /// Divides 2 numbers
        /// </summary>
        /// <param name="a">First number</param>
        /// <param name="b">Second number</param>
        /// <returns>Quotient of 2 numbers</returns>
        public double Divide(double a, double b)
        {
            if (b == 0)
                throw new ArgumentException("Cannot divide by zero!");
            return a / b;
        }

    }
}

There's only the Divide() method interesting in the code, throwing an exception if we divide by zero. The default C# .NET behavior for decimal numbers would be to return the "Infinity" value, which always isn't what the application user expects.

UnitTesting

In C# .NET, we write unit tests using tools from the Microsoft.VisualStudio.TestTools.UnitTesting namespace. Visual Studio provides full support for these tests and we add them to our application as another project to our solution. Like this, the tests are completely separated from the project, which is a great design advantage, but we must not forget to link the projects with the appropriate references.

In the Solution Explorer, we'll click the "CalculatorApp" solution with the right button and choose Add -> New Project...

Adding a new test project in Visual Studio - Testing in C# .NET

The test project name is typically the application project name + the word "Tests", in our case, it's "CalculatorAp­pTests":

A test project for C# .NET - Testing in C# .NET

To the test project, we now need to add a reference to the application project so we can access its classes. To do so, we'll right-click the CalculatorAppTests project and choose Add -> Reference ...

Testing in C# .NET

In the following form, we'll select the Projects -> Solution tab and check the CalculatorApp project. We'll confirm the dialog, making the Calculator class available.

Adding a project reference for testing in Visual Studio - Testing in C# .NET

In the CalculatorAppTest project, a new UnitTest1 file was generated with the following code:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace CalculatorAppTests
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
        }
    }
}

Perhaps you aren't surprised that, in object-oriented C#, class tests (scenarios) are also represented by a class and the individual tests by methods :) We've already encountered attributes (square brackets above methods and classes) in the courses and we know they provide some additional specification. [TestClass] here says the class represents a test scenario, the methods representing individual tests are marked with [TestMethod] (these will be invoked automatically by Visual Studio). We'll rename the class (and its file) to CalculatorTests as it will contain tests for the Calculator class.

Class coverage with tests

We can use several more attributes in our unit tests. We'll use [TestInitialize] and also, for explanation purposes, [TestCleanup], allowing us to mark the methods which will be called before, respectively, after each test in this class. This is very important to us because, according to best practices, we want our tests to be independent. Usually, before each test, we prepare the same environment again to avoid any interference of the tests with each other. We'll discuss good practices in detail later. Let's add a calculator field to the class and, in the method with the [TestInitialize] annotation, we'll always create a new calculator instance for each test. If there were any additional settings or dependencies needed, they would be in this method as well. We'll remove the TestMethod1() method. For the calculator, we'll add using CalculatorApp;:

using System;
using CalculatorApp;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace CalculatorAppTests
{
    [TestClass]
    public class CalculatorTests
    {
        private Calculator calculator;

        [TestInitialize]
        public void Initialize()
        {
            calculator = new Calculator(); // Creates a new calculator before each test
        }

        [TestCleanup]
        public void Cleanup()
        {
        }

}

We have everything ready to add tests, which we'll do in the next lesson, Testing in C# .NET - Finishing unit tests and best practices.


 

Previous article
Introduction to software testing in C# .NET
All articles in this section
Testing in C# .NET
Skip article
(not recommended)
Testing in C# .NET - Finishing unit tests and best practices
Article has been written for you by David Capka Hartinger
Avatar
User rating:
4 votes
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn university David learned IT at the Unicorn University - a prestigious college providing education on IT and economics.
Activities