Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / DevOps / testing

Setting Up Unit Testing

5.00/5 (2 votes)
1 Feb 2016CPOL6 min read 7.2K  
How to set up unit testing

Introduction

In the previous posts, I talked about unit testing. I gave some background, and I explained the flow of Test-driven Development (TDD). So now is the time to set things up. In this article, I will use the calculation of the Greatest Common Divisor or 2 integers as an example. I’m using Visual Studio 2015 to run my examples, but you can do the same in other development environments of course. I’m supposing some basic knowledge about VS2015 (but not much).

Setting Up the Application

In VS, create a new Console Application, name it “GCD”.

image

This will create a solution called “GCD”, with a console application project called “GCD” as well. No surprises there!

Here is the Main() function, and the function under test CalcGCD(). I will number the functions CalcGCD1, CalcGCD2, etc. to indicate the workflow.

C#
class Program
{
    static void Main(string[] args)
    {
        int x;
        int y;

        Console.Write("x: ");
        x = int.Parse(Console.ReadLine());
        Console.Write("y: ");
        y = int.Parse(Console.ReadLine());

        Calc c = new Calc();

        int z = c.CalcGCD1(x, y);

        Console.WriteLine($"GGD({x}, {y}) = {z}");
    }
}

public class Calc
{
    public int CalcGCD1(int x, int y)
    {
        throw new NotImplementedException();
    }
}

Running this first version of the program will terminate it with an exception, which gives us a perfect baseline for our tests.

Adding a Test Project

The easiest way to do this is to right click on the method that you want to test (CalcGCD1) and then choose “Create Unit Tests”. This will show the following dialog box:

image

The default values are perfect in this case, so I won’t change them. To go over the fields:

  • Test Project: A new test project will be created. If you have already a test project in your solution, you can use this one instead. Often, you’ll find yourself with more than one test project: you may want to test your data services, your business services, your UI, … and for each of these projects, you’ll create a separate Unit Test project. When a solution becomes a bit bigger, I usually create a “Test” folder to group all my test projects in.
  • Name Format for Test Project: This will become GCDTests, which is OK.
  • Namespace: In our case: GCD.Tests.
  • Output file: If you have already some tests for this function (or class, as you’ll usually put all the tests for one class together), then you can pick the corresponding test file. As we’re creating our first test here, I select “New Test File”.
  • Name Format for Test Class: should be obvious now.
  • Name Format for Test Method: should be obvious now.
  • Code for Test Method: The 3 options are self-explanatory, we’ll use the default.

Clicking OK gives the following result:

image

A new test project is created, named GCDTests.

In the references, we can see that the GCD project is automatically referenced, which gives us access to the methods in the Calc class.

A first class called CalcTests is generated, which contains 1 method already: CalcGCD1Test. In the “Code for Test Method” drop down, we selected “Assert Failure”, which is what you see here indeed:

C#
[TestMethod()]
public void CalcGCD1Test()
{
    Assert.Fail();
}

The only line is “Assert.Fail();”, which will make our test red in the test runner. Assertions are the heart of unit testing, so I’ll give some more examples later.

One more thing to be noticed: the method is preceded by the [TestMethod] attribute. And the test class is preceded by the [TestClass] attribute. These attributes tell the test runner that CalcTests may contain some test methods, and that CalcGCD1Test is one of those.

We may as well make the test more useful. I also renamed the test function to indicate its purpose:

C#
[TestMethod()]
public void CalcGCD1BasicTest()
{
    // arrange
    int a = 10;
    int b = 5;
    int expected = 5;
    Calc c = new Calc();

    // act
    int actual = c.CalcGCD1(a, b);

    // assert
    Assert.AreEqual(expected, actual);
}

Running this test will fail, because we haven’t implemented the CalcGCD1 yet. But this is an actual test that will stay in the code base. When we change our function later, and even this basic test fails, we’ll know that something went pretty wrong!

Running the Tests

This requires a test runner. In VS, we find this under the “Test” menu. In here, you can run or debug your tests directly, but even handier is the Test Explorer (Test > Windows > Test Explorer). This will open the Test Explorer Window, which will probably be empty. This is because you haven’t built your test project yet. So press Ctrl+Alt+B to fix this.

image

As you can see by the blue icon, the test hasn’t been executed yet. So click on “Run All” and see what happens: The icon turns red because your test has failed. If you don’t know why your test has failed, you can now put a breakpoint after the // act comment and right click the test. In the menu, you choose “Debug” and you’ll be debugging your code directly, without having to enter the numbers in the console, etc. So this will make your debugging cycle shorter.

Fixing the CalcGCD Function

So TDD says that now we have to fix the function in such a way that all the tests will pass. We only have one test, so that’s easy enough:

C#
public int CalcGCD2(int x, int y)
{
    return 5;
}

Build and run the test again, and yes, we have a green test. The GCD of 10 and 5 is indeed 5. So our function handles our basic test well.

Assertions

We verify the necessary conditions for our tests by using the Assert class. This class is your communication with the test runner. When an assertion fails, the test runner will stop running your code. Here are some of its methods:

  • Assert.Fail() - This will fail immediately and turn our test red. No conditions are checked. There is an overload that takes a string as its parameter. This is usually a good idea, so at least you know why something failed when you see the red icon in the test explorer.
  • Assert.Inconclusive() - You can put this at the end of your test method, when you have reached a point where you are not sure if the test is OK or not. It will give an orange icon in the test explorer. Again, it is a good idea to pass a description to the function.
  • Assert.AreEqual() - This has a lot of overloads to test equality between expected values and actual values. If they're not equal, the test fails.

You can find the full list of assertion methods here.

Parameterized Unit Tests

Some testing frameworks (like NUnit) have the possibility to easily add different test cases using the same function. A test function can look like:

C#
[Theory]
[InlineData(10, 5, 5)]
[InlineData(5, 10, 5)]
[InlineData(7, 5, 1)]
[InlineData(5, 7, 1)]
public void TestGCDNormalCases(int a, int b, int expected)
{

//

}

Unfortunately, VS doesn’t provide an easy way to do this. If you want to work with different sets of data for testing the same function, you have to set up an external data source and then reference this in your tests.

This is out of scope for this article, but I’ll get back to it, and to some other possibilities to fix this problem.

Microsoft, if you read this, we’re waiting for this feature already a long time!

Conclusion

So now, we have set up some basic unit testing for our project. We can add more tests and refine our function further, proving that all the previous tests still pass. And then, we can move on to the next function to be implemented. When you have finished some more functions, you’ll probably start to refactor your code. While refactoring the code, you can run all the tests to verify that everything remains green. And if it doesn’t, you have the tests to help you find out why not.

Image 5 Image 6

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)