我可以使用 NUnit 和 NCrunch 来测试带有预处理器指令的多个构建配置吗?



我正在为一些软件编写API包装库,这些软件具有API的多个版本,并且在多个平台之间进行了大量共享调用,这些平台是同时但单独开发的。随着时间的推移,他们甚至已经将平台合并为使用相同的代码库,只是在不同的名称空间&*下。exe生成。

我通过编写一个单独的代码库来实现这一点,然后通过构建配置使用预处理器指令和条件编译符号来选择性地为构建使用某些代码。大约90%的代码可以在版本和平台之间重复使用,因此这很有帮助。测试中的项目一切顺利。

然而,我在使用NUnit&NCrunch将对该项目进行单元测试。我创建了相同的构建配置来加载正确的常量,并为Integration Test项目创建正确的构建文件夹。然而,我注意到两个奇怪的问题:

  1. NUnit似乎忽略了Integration Test项目中的预处理器指令。例如,在以下示例中,无论配置如何(例如BUILD_Bar_2015=true),代码的第一行总是命中(BUILD_Foov16=true),即使在Visual Studio中,所需的行集(对应于当前配置变量)似乎是唯一活动的行集:

    [TestFixture]
    public class FooBarIncApplicationTests
    {
    #if BUILD_Foov16
    public const string path = @"C:Program Files (x86)FooBarIncFooV16Foo.exe";
    #elif BUILD_Foov17
    public const string path = @"C:Program Files (x86)FooBarIncFooV17Foo.exe";
    #elif BUILD_Bar_2013
    public const string path = @"C:Program Files (x86)FooBarIncBar 2013Bar.exe";
    #elif BUILD_Bar_2015
    public const string path = @"C:Program Files (x86)FooBarIncBar 2015Bar.exe";
    #endif
    [Test]
    public void FooBarIncApplication_Initialize_New_Instance_Defaults()
    {
    using (FooBarIncApplication app = new FooBarIncApplication(path))
    {
    ...
    }
    }
    }
    
  2. 此外,当我通过NCrunch运行测试时,它似乎只使用与列出的第一个配置创建的构建相对应的*.dll(例如,它总是测试为Foo.exe v16编译的*.dll)。

在我看来,这两个问题似乎是相关的。我想知道NUnit和/或NCrunch是否无法处理这样的设置,或者我是否应该用一种特殊的方式来处理这种独特的设置?

我更大的问题是#2,即NCrunch似乎只在从第一个配置构建的*.dll上运行NUnit,这使得无法测试任何其他配置。也许这是项目依赖关系的问题?(上面例子中的路径是我通过API与之交互的程序,但不是我的*.dll项目。)

好的,我发现了问题所在。如果你曾经测试过NCrunch的多个DLL构建配置,这是值得知道的!

当NCrunch运行时,生成的*.dll和确定的代码覆盖率(并逐步执行)不受Visual Studio中指定的当前配置的影响。它始终由Visual Studio加载项目时的项目默认配置决定。这意味着,为了更改测试配置,您需要修改*.csproj文件。

例如,我有Foov16和Foov17的配置定义,如下所示。为了以NCrunch可以工作的方式将项目设置为Foov17,Foov17的配置必须在默认配置(第一个元素)中引用:

<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug-Foov17</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{...}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>FooBarInc.API</RootNamespace>
<AssemblyName>FooBarInc.API</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug-Foov16|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>binAnyCPUDebug-Foov16</OutputPath>
<DefineConstants>TRACE;DEBUG;BUILD_Foov16</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<DocumentationFile>binAnyCPUDebug-Foov16FooBarInc.API.XML</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug-Foov17|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>binAnyCPUDebug-Foov17</OutputPath>
<DefineConstants>TRACE;DEBUG;BUILD_Foov17</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<DocumentationFile>binAnyCPUDebug-Foov17FooBarInc.API.XML</DocumentationFile>
</PropertyGroup>

不确定这是否对您有帮助。如果我理解正确,你想在测试用例中注入不同的exe路径,并让测试用例为每个exe路径运行。您可以尝试为此使用NUnitsTestCase属性。NUnit网站示例:

[TestCase(12,3,4)]
[TestCase(12,2,6)]
[TestCase(12,4,3)]
public void DivideTest(int n, int d, int q)
{
Assert.AreEqual( q, n / d );
}

基于此,您可以将测试重写为:

[TestCase(@"C:Program Files (x86)FooBarIncFooV16Foo.exe")]
[TestCase(@"C:Program Files (x86)FooBarIncFooV17Foo.exe")]
public void FooBarIncApplication_Initialize_New_Instance_Defaults(string path)
{
using (FooBarIncApplicatio app = new FooBarIncApplicatio(path))
{
...
}
}

在此基础上,测试用例将使用exe的版本16和版本17运行。额外的好处:您不必切换VS配置来运行所有测试。

如果您仍然希望使用条件测试用例执行,您可以使用CategorySystem.Diagnostics.Conditional属性。有关更多信息,请参阅此stackoverflow链接。

希望能有所帮助。