当您说“在我们的 CI 服务器上”时,这并不能告诉我们您是如何运行测试的。但是,我似乎在使用vstest.console.exe
.
我发现 MEF 的自动打开类型关闭功能(.net-4.5 中的新功能)在使用 .net-4.5 运行测试时似乎会中断vstest.console.exe
。基本上,vstest.console.exe
可能决定在 .net-4.0 兼容模式下运行,就好像/Framework:Framework40
已传递给它一样。.net-4.5 程序集即使在由 .net-4.0 运行时加载时也能正常加载和运行,但 MEF 检测到您在 .net-4.0 模式下运行并禁用其对自动关闭打开的泛型类型的支持。您可以通过传递开关强制vstest.console.exe
在 .net-4.5 模式下运行,让 MEF 再次正常运行。/Framework:Framework45
(这在安装了 .net-4.6.1 的机器上进行了测试,但不确定这是否会改变更多)。
最小复制
当我针对 .net-4.5 框架制作一个简单的测试程序集时,vstest.console.exe
正确地猜测它应该使用 .net-4.5 运行测试。但是,当我制作一个集成到更复杂的构建环境中的程序集时,vstest.console.exe
突然开始以 .net-4.0 模式而不是 .net-4.5 模式运行。因此,我从复杂的构建环境开始,并开始缩减内容,直到问题消失。
要vstest.console.exe
猜测错误的框架,您需要两个程序集。一方面,定义一个自定义程序集Attribute
。另一方面,应用该自定义程序集属性并定义您的单元测试。
第一次组装:
using System;
[AttributeUsage(AttributeTargets.Assembly)]
public class BreakVSTestFrameworkDetectionAttribute : Attribute
{
}
引用前一个的第二个程序集:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
[assembly: BreakVSTestFrameworkDetection]
[InheritedExport]
public interface IGenericService<T>
{
void Print(T thing);
}
public class SomeGenericService<T> : IGenericService<T>
{
public void Print(T thing) => Console.WriteLine($"{typeof(T)}:{thing}");
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void Run()
{
using (var catalogue = new ApplicationCatalog())
using (var container = new CompositionContainer(catalogue))
{
container.GetExportedValue<IGenericService<string>>().Print("asdf"); // System.String:asdf
container.GetExportedValue<IGenericService<int>>().Print(123); // System.Int32:123
}
}
static void Main(string[] args) => new UnitTest1().Run();
}
通过这两个程序集和定义的测试,我在不同的场景中得到以下结果:
使用 Visual Studio 2015 Community Editions 的 GUI 运行测试工作得非常好。
直接将代码作为可执行文件运行可以正常工作。
C:\Users\ohnob\OneDrive\Documents\Visual Studio 2015\Projects\MefGenericsUnitTests>bin\Debug\MefGenericsUnitTests.exe
System.String:asdf
System.Int32:123
使用类似 mstest 的东西运行测试(除了我不知道为什么会或不会使用它而不是 vstest)神奇地起作用(至少在这种情况下)(输出修剪)。
C:\Users\ohnob\OneDrive\Documents\Visual Studio 2015\Projects\MefGenericsUnitTests>"\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\MSTest.exe" /testcontainer:bin\Debug\MefGenericsUnitTests.exe
Microsoft (R) Test Execution Command Line Tool Version 14.0.23107.0
Copyright (c) Microsoft Corporation. All rights reserved.
Loading bin\Debug\MefGenericsUnitTests.exe...
Starting execution...
Results Top Level Tests
------- ---------------
Passed UnitTest1.Run
1/1 test(s) Passed
直接运行vstest.console.exe
失败(输出修剪):
C:\Users\ohnob\OneDrive\Documents\Visual Studio 2015\Projects\MefGenericsUnitTests>"\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe" bin\Debug\MefGenericsUnitTests.exe
Microsoft (R) Test Execution Command Line Tool Version 14.0.24720.0
Copyright (c) Microsoft Corporation. All rights reserved.
Starting test execution, please wait...
Failed Run
Error Message:
Test method UnitTest1.Run threw exception:
System.ComponentModel.Composition.ImportCardinalityMismatchException: No exports were found that match the constraint:
ContractName IGenericService(System.String)
RequiredTypeIdentity IGenericService(System.String)
但vstest.console.exe /Framework:Framework45
成功:
C:\Users\ohnob\OneDrive\Documents\Visual Studio 2015\Projects\MefGenericsUnitTests>"\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe" /Framework:Framework45 bin\Debug\MefGenericsUnitTests.exe
Microsoft (R) Test Execution Command Line Tool Version 14.0.24720.0
Copyright (c) Microsoft Corporation. All rights reserved.
Starting test execution, please wait...
Passed Run
Total tests: 1. Passed: 1. Failed: 0. Skipped: 0.
Test Run Successful.
Test execution time: 3.7855 Seconds