如何在 Visual Studio 中快速调试 NUnit 测试






4.67/5 (6投票s)
一篇关于快速调试 NUnit 测试的文章。
引言
使用 NUnit 调试单元测试可能会很痛苦。
首先,您必须在解决方案资源管理器中找到项目并将其设为启动项目。然后,您运行项目,等待 NUnit 加载几秒钟,然后从(可能有很多同名测试的)树中选择要运行的测试。这假设您一开始就已设置好测试项目,这本身就很麻烦!当然,您会忘记测试项目是启动项目,当您想运行解决方案中的主项目时,测试项目却会运行!这非常令人烦恼,我遇到过几十次了。
整个过程耗时太长。您真正想做的是即时调试测试,而无需启动 NUnit。这正是这个插件试图实现的。
背景
该代码是 Visual Studio 的一个插件,它扩展了我上一篇文章“如何在 Visual Studio 中运行解决方案中的所有 NUnit 测试”中描述的插件。
安装
将演示文件解压到某个文件夹。将“Jonno Nunit Helper.AddIn”文件移动到“您的路径\我的文档\Visual Studio 2008\Addins”文件夹。打开该文件并更改以下行,使其指向 Jonno.AddIns.NunitHelper.dll 文件的位置
<Assembly>C:\JonnoTest\Jonno.AddIns.NunitHelper.dll</Assembly>
该代码扩展了我先前自动在生成后运行 NUnit 的插件。如果您不希望发生这种情况,请打开“test framework helper options.xml”文件并将以下内容更改为“false”
<RunTestsAfterBuild>true</RunTestsAfterBuild>
要更改 NUnit 的版本或位置,请更改以下行
<TestRunnerPath>C:\Program Files\NUnit 2.5.2\bin\net-2.0\nunit.exe</TestRunnerPath>
如果您正在运行源代码,那么“Jonno Nunit helper.AddIn”文件很可能不存在。将其添加到插件项目中,确保您链接到插件文件夹中的版本,而不是添加副本。
如果您希望运行单元测试,则需要 NUnit 2.5 和 Rhino Mocks 3.6。我只在 Visual Studio 2008 上测试过此功能。
选项
如果当前代码窗口中的选择点位于 NUnit 测试内部,则在右键单击上下文菜单中,将有一个新选项“Jonno - Debug Current test”。在测试文本内设置一个断点,使用菜单选项,您应该能够立即调试您的测试。NUnit 测试被定义为:一个方法是 public
的,不是 static
的,没有参数,返回类型是 void
,并且具有 Test
属性。
在测试调试完成后,上下文菜单将添加另一个选项“Jonno - Debug last test”。这只会再次运行最后一个测试。如果断点仍然存在,它将再次进入调试模式。如果您更改了测试正在测试的类,这将非常有用,因为您可以立即再次调试测试以查看它是否有效,而无需导航到测试本身。
在这两种情况下,如果您对代码进行了更改,插件都应该能够识别并先编译项目再进行调试。
Using the Code
该插件工作原理的核心是创建一个名为 Jonno.SimpleTestRunner.exe 的新进程。SimpleTestRunner 使用反射来查找我们要测试的程序集中的方法,并创建一个类实例。然后,它使用反射运行测试方法(以及该类拥有的任何 Setup
、TearDown
、TestFixtureSetup
或 TestFixtureTeardown
方法)。该插件使用 Visual Studio 内置的“附加到进程”功能,在创建进程后立即将 Visual Studio 调试器附加到该进程。SimpleTestRunner 在运行测试之前会检查是否有调试器附加到它。因此,当调用测试方法时,它应该在 Visual Studio 调试器中执行,并且我们可以调试测试!
在 Connect
类中创建 SimpleTestRunner 进程,如下所示
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;
}
为了使每个类都能在 Visual Studio 调试器中显示,解决方案中的每个项目都必须在 SimpleTestRunner
类中加载其对应的程序集。项目程序集路径已从传递给 exe 的第四个参数开始传递
// 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);
}
}
检查调试器是否已附加也很容易。在这种情况下,如果在三秒钟内没有附加调试器,我们就假设它失败了
// 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;
}
}
实际的反射和测试运行由 Jonno.SimpleTestRunner.Logic 项目处理。反射已经讨论过很多次了,所以我不再赘述。
一如既往,插件中的 Connect
类负责处理 Visual Studio 事件和菜单等。我已经将许多用于这些用途的代码移到了 Jonno.AddIns.Logic.VisualStudioSpecific 项目中。
COM EnvDTE
对象及其子类是 Visual Studio 可扩展性的核心。我觉得它很难用,因为它非常庞大,文档零散,并且似乎无法对使用它的类进行单元测试。
Jonno.AddIns.Entities 项目包含封装对象使用的类,通过向其提供一个外观。例如,以下(已编辑)代码检查当前选择是否为测试方法
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))
{
这比直接进入 DTE2.ActiveWindow
对象的 ProjectItem
的 FileCodeModel
,并迭代 CodeElements
,然后强制转换为 CodeMethod
等要容易得多,也更容易理解。
Jonno.Utilities 项目包含用于将选项读/写到 XML 以及创建和终止进程的类。
限制
SimpleTestRunner 的命名非常贴切,因为它非常简单。它只支持 NUnit 和以下属性:TestFixtureSetup
、Setup
、Test
、Teardown
和 TestFixtureTeardown
。我从未用过这些以外的任何属性。如果您需要其他属性,修改测试运行程序将很容易做到。如果您愿意,您也可以轻松地扩展它以支持其他测试框架。
有时调试器无法正确附加。如果发生这种情况,请等待几秒钟,然后重试调试测试,它可能会起作用。我不知道为什么会发生这种情况,我猜测这可能与调试器本身有关,但也可能与我的代码有关。如果有人能指点我原因,我将非常高兴。
历史
- 2009 年 9 月 29 日:初始版本。