Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How to quickly debug a NUnit test in Visual Studio

0.00/5 (No votes)
30 Sep 2009 6  
An article on quickly debugging NUnit tests.

Sample Image

Introduction

Debugging unit tests using NUnit can be a pain.

First, you have to find the project in the Solution Explorer and make it the Startup project. Then, you run the project and wait several seconds for NUnit to load, before picking the test you want (from possibly many similarly named tests) in the tree and running it. This assumes that you have set up the test project to do this in the first place, which itself is an annoyance! Of course, you forget that the test project is the startup, and when you want to run the main project in your solution, the test project runs instead! Extremely annoying, and has happened to me dozens of times.

The whole process simply takes too long. What you really want to do is debug the test on the fly, without having to start NUnit at all. This is what this add-in tries to achieve.

Background

The code is an add-in to Visual Studio which expands on the add-in I described in my previous article "How to run all your NUnit tests in a solution in Visual Studio".

Installation

Unzip the demo files to a folder somewhere. Move the "Jonno Nunit Helper.AddIn" file to your "your path\My Documents\Visual Studio 2008\Addins" folder. Open the file up and change the following line to point to the location of the Jonno.AddIns.NunitHelper.dll file:

<Assembly>C:\JonnoTest\Jonno.AddIns.NunitHelper.dll</Assembly>

The code expands upon my previous add-in which runs NUnit automatically after a build. If you don't want this to happen, open the "test framework helper options.xml" file and change the following to "false":

<RunTestsAfterBuild>true</RunTestsAfterBuild>

To change the version or location of NUnit, change the following line:

<TestRunnerPath>C:\Program Files\NUnit 2.5.2\bin\net-2.0\nunit.exe</TestRunnerPath>

If you are running the source, then the "Jonno Nunit helper.AddIn" file will probably be missing. Add it to the add-in project, making sure you link it to the version in your add-ins folder, rather than adding it.

If you wish to run the unit tests, you will need NUnit 2.5 and Rhino Mocks 3.6. I have only tested this on Visual Studio 2008.

Options

If the selection point in the current code window is within a NUnit test, then under the right mouse click context menu, there will be a new option "Jonno - Debug Current test". Set a break point within the text of the test, use the menu option, and you should be able to immediately debug your test. A NUnit test is defined as a method that is public, not static, with no parameters, has a return type of void, and has the Test attribute.

After a test has been debugged, then another option will be added to the context menu "Jonno - Debug last test". This will simply run the last test again. If the break points are still there, it will go into Debug mode again. This is useful if you make a change to the class that the test is testing, as you can immediately debug the test again to see if it works, without having to navigate to the test itself.

In both cases, if you have made changes to the code, the add-in should recognise this and compile the projects before debugging.

Using the Code

The core of how the add-in works is to create a new process called Jonno.SimpleTestRunner.exe. The SimpleTestRunner uses Reflection to find the method to test in the assembly we want, and creates an instance of the class. It then uses Reflection to run the test method (as well as any Setup, TearDown, TestFixtureSetup, or TestFixtureTeardown method that the class has). The add-in uses the built-in "Attach to Process" functionality of Visual Studio to attach the Visual Studio debugger to the process, just after it is created. The SimpleTestRunner checks to see if a debugger is attached to it just before running the test. So, when the test method is called, it should execute in the Visual Studio debugger, and we can debug the test!

Creating the SimpleTestRunner process in the Connect class, as follows:

var processFactory = new ProcessesFactory();
var process = processFactory.Create();

// kill any existing process and start a new one
process.Kill(SimpleTestRunnerWithoutExtension);
process.Start(this.AddInPath + SimpleTestRunner, arguments);

// attach to the new process with the debugger
if  (!StandardConnectionMethods.AttachToLocalProcess(
                this.ApplicationObject, SimpleTestRunner))
{
    // if can't attach then kill the just started process
    process.Kill(SimpleTestRunnerWithoutExtension);
    return;
}

For every class to show in the Visual Studio debugger, each project in the solution must have its corresponding assembly loaded in the SimpleTestRunner class. The project assembly path has been passed in from the fourth argument to the exe onwards:

// load all the projects into memory so that
// the debugger will enter them if required to
var list = new List<Assembly>();

for (int i = 4; i < args.Length; i++)
{
    var assembly = Assembly.LoadFrom(args[i]);

    if (assembly != null)
    {
        list.Add(assembly);
    }                    
}

Checking that a debugger is attached is easy as well. In this case, if one is not attached within three seconds, we assume that it has failed:

// wait for Visual Studio to attach to this process 
// wait for three seconds before deciding that it won't happen
while (!Debugger.IsAttached)
{
    j++;
    Thread.Sleep(100);

    if (j == 30)
    {
        MessageBox.Show("Could not attach to debugger.\r\nTry again.", "Try again.", 
                        MessageBoxButtons.OK, MessageBoxIcon.Information);
        return;
    }
}

The actual reflection and running of the test is handled in the Jonno.SimpleTestRunner.Logic project. Reflection has been covered many times before, so I won't go over that.

As usual, the Connect class in the add-in handles the Visual Studio events and menus etc. I have moved a lot of code that is repeatedly used for such things into the Jonno.AddIns.Logic.VisualStudioSpecific project.

The COM EnvDTE object and its subclasses are the heart of Visual Studio extensibility. I find it quite hard to use as it is very large, the documentation is spotty, and seems impossible to unit test classes that use it.

The Jonno.AddIns.Entities project contains classes which encapsulate the use of the object, by presenting a facade to it. For example, the following (edited) code checks to see if the current selection is a test method:

var factory = new CodeFileFactory();
this.ActiveWindowCodeFiles = 
  factory.CreateFromWindow(this.ApplicationObject, window);

var selected = this.ActiveWindowCodeFiles.Current.GetCodeElementAtSelection();

if (selected.Kind == ElementType.Method &&
    selected.Access == AccessType.Public &&
    selected.IsShared == false &&
    selected.HasAttribute(TestAttribute))
{

This is much easier to do and to understand than directly going into the FileCodeModel of the ProjectItem of the DTE2.ActiveWindow object, and iterating the CodeElements, and then casting to CodeMethod etc.

The Jonno.Utilities project contains classes for reading/saving options to XML, and creating and killing processes.

Limitations

The SimpleTestRunner is well named as it is very simple. It only supports NUnit and the following attributes: TestFixtureSetup, Setup, Test, Teardown, and TestFixtureTeardown. I have never used anything other than these. If you need another attribute, the test runner will be easy enough to modify to do so. You should also be able to easily extend it to support other test frameworks if you wish.

Sometimes the debugger does not attach correctly. If this happens, then wait a few seconds and try to debug the test again, it will probably then work. I do not know why this happens, my guess is that it is something to do with the debugger itself, though it may be something to do with my code. I will be pleased if someone can enlighten me as to the reason.

History

  • 29th September, 2009: Initial version.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here