Basic Unit Testing in C# with Visual Studio

Unit testing is the process of creating low-level function checks for your code. The key idea is to test the lowest possible level of methods to make it simpler to troubleshoot and find the root of bugs. This usually equates to testing each of your methods, no matter how trivial they may seem. Unit testing is also an important aspect of Test Driven Development (TDD).

TDD reverses the standard workflow by suggesting that you create tests first prior to writing any code. At first glance, this may seem counter-intuitive, however,  there are several advantages to this methodology. With requirements and specific tests to pass defined up front, you will have a direct idea of what needs to be implemented and what results you should get from your code. Whether you decide to follow the TDD doctrine of defining tests first or if you choose to create tests after writing your code, a robust unit test suite will help validate the accuracy of your code in a quick and efficient manner.

C# combined with visual studio provides a great project environment for development and unit testing. While this tutorial will be using MSTest projects for the unit tests, there are other options out there; each language has its own testing frameworks. 

If you run into any issues during this tutorial, you can always clone this project off of my GitHub repository.

To follow TDD, we’ll create tests first. Keep in mind, there’s nothing wrong with developing your code first and creating tests later on. It will be primarily the same process.

Open Visual Studio and create a MSTest Project.

Pay attention to your Project Name and Solution Name. Solution is the overarching organization and can contain multiple projects. In this context it will house the unit tests and the class that it’s testing.

Once the unit test is created you’ll get a default file as seen below.

using HappySnailSimpleMath;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace HappySnailSimpleUnitTests
{
[TestClass]
public class SimpleUnitTests
{
[TestMethod]
public void TestAddNums()
{
//Ints
Assert.AreEqual(SimpleMath.AddNums(2, 3), 5);
Assert.AreEqual(SimpleMath.AddNums(0, 0), 0);
Assert.AreEqual(SimpleMath.AddNums(0, -1), -1);
Assert.AreEqual(SimpleMath.AddNums(-1, -1), -2);
//Doubles
Assert.AreEqual(SimpleMath.AddNums(.5, .2), .7, .0001);
}
[TestMethod]
public void TestDivideNums()
{
Assert.AreEqual(SimpleMath.DivideNums(5.0, 2.5), 2.0);
Assert.AreEqual(SimpleMath.DivideNums(-5.0, 2.5), -2.0);
Assert.AreEqual(SimpleMath.DivideNums(5.0, -2.5), -2.0);
}
[TestMethod]
public void TestIsPrimeNum()
{
Assert.IsTrue(SimpleMath.IsPrime(2));
Assert.IsTrue(SimpleMath.IsPrime(3));
Assert.IsTrue(SimpleMath.IsPrime(5));
Assert.IsTrue(SimpleMath.IsPrime(7));
Assert.IsTrue(SimpleMath.IsPrime(11));
Assert.IsFalse(SimpleMath.IsPrime(4));
}
}
}

You can add this code to your newly created test file.

Before moving forward, I want to go through some of the unit test syntax. The [TestClass] and [TestMethod] are called attributes.

[TestClass]
 public class SimpleUnitTests 
 ...
 [TestMethod]
 public void TestAddNums()

Attributes associate system or user-defined information with a target element, in this case, the class and methods that the attribute is above. The Assert method is the bread and butter of the unit test. It checks to see that the subsequent methods return true. When the unit test is run, the assert statements are what determines whether the test passes or fails.

Assert.AreEqual(SimpleMath.AddNums(2, 3), 5);
 Assert.AreEqual(SimpleMath.AddNums(.5, .2), .7, .0001);

AreEqual takes in two parameters and checks if they’re equal. It can also take a third parameter that is used in the case of testing doubles. The third parameter checks for a delta in the results. This can be important for doubles and floats as they don’t always perfectly equal each other, and may be off by .000001 or some other small amount. MSTest seems to resolve asserts correctly for doubles without the delta, but it’s something to keep in mind if you run into issues.

Assert.IsTrue(SimpleMath.IsPrime(11));
 Assert.IsFalse(SimpleMath.IsPrime(4));

The other two methods used are IsTrue and IsFalse, they are both self-explanatory.

When developing unit tests, you always want to test for edge cases. For the simple math methods we will implement, it may be testing with 0 or negative numbers. For other methods being tested, edge cases may involve sending in null objects or whatever you think of that will test the resiliency of your methods.  

You should notice now that the methods are underlined red, due to them not being defined. We have our tests written, but no functions to test. So, in TDD fashion, when we have tests not working, we need to add some code to make the tests pass!

Add a new project to the solution. Remember, that a solution can hold multiple projects at the same time. This will allow the unit test to easily reference the math methods in the same solution.

Select a new Console Application.

Solution name should be the same as the unit test’s, while the project name should be unique to the math methods that are being created for distinction and organization. 

There will only be a simple Hello World console application, so a new file needs to be added to create the math methods.

Create an empty class with a suitable title; I labeled it ‘SimpleMath’.

namespace HappySnailSimpleMath
{
public class SimpleMath
{
public static int AddNums(int num1, int num2)
{
return num1 + num2;
}
public static double AddNums(double num1, double num2)
{
return num1 + num2;
}
public static double DivideNums(double num1, double num2)
{
return num1 / num2;
}
public static bool IsPrime(int num)
{
if (num < 2)
return false;
for (int i = 2; i < num; i++)
{
if (num % i == 0)
return false;
}
return true;
}
}
}
view raw SimpleMath.cs hosted with ❤ by GitHub

Add this code to the newly created SimpleMath file. Ensure the class is public and that the methods are static. If the methods are static, the methods can be referenced without creating an object of the class, and it can be treated more like a utility class than an object-oriented one.

We’re almost done, but we still need to add references for the unit test. The unit test needs a reference to the SimpleMath class in order to utilize its methods. So add a reference to the Dependencies folder within the unit test project.

Ensure that the SimpleMath project is check marked and press OK.

If you are still seeing red underlines, you need to ensure that you add a using statement at the top of the unit test to reference the namespace of the SimpleMath file. Namespaces are a way to define and organize the scope of classes and methods.

Once you’ve properly referenced the SimpleMath class, we can run the unit tests. You can either access the unit tests tab on the right side of the screen or select View->Pads->Unit Test. When you’re ready select the green arrow to run the unit tests (see below).

Here’s the Test Results window that should pop up towards the bottom of the screen. It will let you know what tests passed and/or failed. An important note to remember is that it will only tell you the test method that failed, not the individual line. So it may not always be the best idea to throw 50 assert methods in a single test method.

That finishes the basics of running unit tests! Even for something as simple as the SimpleMath methods, unit tests can be highly effective. Instead of wasting tons of time trying to test output with console writelines/logs, you can develop a unit test to ensure your methods always work correctly. Unit tests become even more imperative in larger and more complex systems. If new code is added in one part of the system, you can still ensure that it doesn’t negatively impact any portion of the system by running system wide unit tests. Unit test failures can also pinpoint exactly where a bug or issue may be in the code, down to the method that can severely cut down on troubleshooting time. Overall, while unit testing may not be the most exciting aspect of software engineering, it’s an extremely vital one that is used in professional environments and can help drive success to you and your personal projects.

Leave a comment