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

The (Probably) Simplest Intro to Visual Studio Unit Testing Ever Written

4.00/5 (4 votes)
25 Feb 2016CPOL4 min read 14.2K  
This is a bare-bones, step-by-step tutorial into getting your first test project set up in a Visual Studio solution that already has a project that you want to test

Toe-dipping into the MS Test Waters

Note: This is an example of "test second" (not "test-first") methodology.

Here is all you need to do to get started with using a Testing project in Visual Studio (2013, that is, although it probably works the same, or near enough to the same, in other versions of VS so as to be nary worth a note):

Right-click a solution which already contains a project that you want to test, and select Add | New Project...Select Visual C# | Test

Give the test project the same name as the existing project it will test + ".Tests"; e.g., if the project you are going to test is named "RoboReporter", you could name it "RoboReporter.Tests". Of course, you could really name it just about anything you want, even "YourMamaWearsCombatBootsUsedToBeConsideredAPejorative.ThisIsNotATestNoReallyItIsInFact" - but why would you?

Your main app doesn't need to know about the Test project, but the Test project does need to know about the main project, so add a Reference to the project you're going to test. Right-click References | Add Reference..., and select Solution | Projects | and then your project (such as RoboReporter).

Now you are at the point where you can add test code, in the "UnitTest1.cs" file that has been created for you (you can rename it if you want and/or add another class). If you opt to change the name to make its purpose clearer (recommended), you can name the class "UtilitiesTest" or "TheKalamazooKazoosPlayAtTheZoosAndGetPooOnTheirShoes" or whatever floats your dinghy.

Microsoft [Rocks Like a Hurricane Shelter from the Storm] Windows

Add a "using Microsoft.VisualStudio.TestTools.UnitTesting;" to the top of the class if it's not already there, and also a reference to the main project (such as "using RoboReporter;")

Add a "TestClass" attribute above the class name so that your class looks like this (assuming your app is named "RoboReporter" and you named the class "NUnitTest":

C#
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RoboReporter;

namespace RoboReporter.Tests
{
    [TestClass]
    public class UtilitiesTest
    {
    }
}

Add a method that will instantiate a "foreign" class in your project and call one of its methods. First, add a "TestMethod" attribute to the method so that it looks like this:

C#
[TestClass]
public class UtilitiesTest
{
    [TestMethod]
    public void TestGetNextWeekday()
    {
    }
}

Add some code to test your assumptions about the method you're testing, and add an "Assert" statement to the bottom of the method, like so:

C#
public void TestGetNextWeekday()
{
    DateTime pseudoRandomDate = new DateTime(2016, 02, 24);
    DayOfWeek dow = DayOfWeek.Monday;
    DateTime retVal = RoboReporterConstsAndUtils.GetNextWeekday(pseudoRandomDate, dow);
    // The value returned should be 2/29/2016
    DateTime expectedVal = new DateTime(2016, 02, 29);
    Assert.AreEqual(expectedVal, retVal);
}

Now your entire test class should look something like:

C#
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RoboReporter;

namespace RoboReporter.Tests
{
    [TestClass]
    public class UtilitiesTest
    {
        [TestMethod]
        public void TestGetNextWeekday()
        {
            DateTime pseudoRandomDate = new DateTime(2016, 02, 24);
            DayOfWeek dow = DayOfWeek.Monday;
            DateTime retVal = RoboReporterConstsAndUtils.GetNextWeekday(pseudoRandomDate, dow);
            // The value returned should be 2/29/2016
            DateTime expectedVal = new DateTime(2016, 02, 29);
            Assert.AreEqual(expectedVal, retVal);
        }
    }
}

Note: Besides "Assert.AreEqual()", there are other "Assert"s you can use to test your methods, such as:

  • Assert.NotEqual
  • Assert.IsFalse
  • Assert.IsTrue
  • Assert.Null
  • Assert.NotNull

Finally, run the test to verify that your assumption (assertion) is correct; select Test | Run | All Tests (or mash Ctrl+R, A). If you don't see the results in the Test Explorer window, you can by selecting Test | Windows | Test Explorer. If the test passes, you will see a green arrow icon next to your test; if it fails, you will peer bemusedly at a circular "red X" icon. To test that, you can provide a bogus answer as the expected answer such as changing "expectedVal" to something other than "DateTime(2016, 02, 29)" such as by changing the "29" to "28" or something like that.

If you want to test the same method as in the example above, it is:

C#
// from Jon Skeet (http://stackoverflow.com/questions/6346119/datetime-get-next-tuesday)
public static DateTime GetNextWeekday(DateTime start, DayOfWeek day)
{
    // The (... + 7) % 7 ensures we end up with a value in the range [0, 6]
    int daysToAdd = ((int)day - (int)start.DayOfWeek + 7) % 7;
    return start.AddDays(daysToAdd);
}

This is just scratching the surface, but it shows how test methods can be incorporated into your Visual Studio solutions; keeping this tip brief holds to the attestation (No pun intended) that this is the shortest of all Test toedippings ever seen by modern (not to mention neanderthal) man (not to mention woman).

Note: To be able to test methods with the "internal" access modifier (to avoid changing them to "public"), add the following to the \Properties\AssemblyInfo.cs file:

C#
[assembly: InternalsVisibleTo("RoboReporter.Tests")]

(this assumes your test project is named "RoboReporter.Tests").

Side Dishes

A side benefit of viewing the tests in the Test Explorer is it tells you how long it takes for each test to run, which gives you a good idea of which methods may need to be revisited/refactored for performance tuning.

There are a couple of more side benefits that accrue to you when adding Test projects to your solutions: It (practically) forces you to write separate methods to call from your event handlers, rather than putting the event handling code right there (it's hard to call an event handler from a test project, for which you would have to pass a sender and event args). It's a good thing to do it that way, anyway (call separate methods from event handlers), because you may need to call that code from elsewhere, anyway (not just from the event handler - other actions can require the same code to run). This is, of course, if you don't actually use "sender" and the event args in your code.

Who Let the Bugs Out?

If a test is failing and you dont' know why, put a breakpoint in it and then select Test | Debug | All Tests or just mash Ctrl+R, Ctrl+A

Now you can take it from here and test to your heart's contentiousness.

License

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