65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (6投票s)

2009年9月30日

CPOL

5分钟阅读

viewsIcon

47167

downloadIcon

470

一篇关于快速调试 NUnit 测试的文章。

Sample Image

引言

使用 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 使用反射来查找我们要测试的程序集中的方法,并创建一个类实例。然后,它使用反射运行测试方法(以及该类拥有的任何 SetupTearDownTestFixtureSetupTestFixtureTeardown 方法)。该插件使用 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 对象的 ProjectItemFileCodeModel,并迭代 CodeElements,然后强制转换为 CodeMethod 等要容易得多,也更容易理解。

Jonno.Utilities 项目包含用于将选项读/写到 XML 以及创建和终止进程的类。

限制

SimpleTestRunner 的命名非常贴切,因为它非常简单。它只支持 NUnit 和以下属性:TestFixtureSetupSetupTestTeardownTestFixtureTeardown。我从未用过这些以外的任何属性。如果您需要其他属性,修改测试运行程序将很容易做到。如果您愿意,您也可以轻松地扩展它以支持其他测试框架。

有时调试器无法正确附加。如果发生这种情况,请等待几秒钟,然后重试调试测试,它可能会起作用。我不知道为什么会发生这种情况,我猜测这可能与调试器本身有关,但也可能与我的代码有关。如果有人能指点我原因,我将非常高兴。

历史

  • 2009 年 9 月 29 日:初始版本。
© . All rights reserved.