TL;DR, 问题:
.NET中扩展方法的存在会对代码的执行产生什么影响(例如JIT/优化)?
背景
我在MSTest中遇到了一个测试失败,这取决于是否还测试了另一个似乎无关的程序集。
我注意到了测试失败,并偶然发现如果加载了另一个测试程序集,则只会发生失败。在4.5 CLR下,运行Unittests和Integration test程序集的mstest将开始执行集成测试,并在第21个集成测试下失败,而在4.0 CLR下不会发生这种情况(除配置外相同)。 我从集成测试程序集中删除了除失败的测试之外的所有测试。现在,当两个测试程序集都加载时,mstest会加载两个程序集,然后执行集成测试程序集中的单个测试,该测试会失败。
> mstest.exe /testcontainer:Unittests.dll /testcontainer:IntegrationTests.dll
Loading C:\Proj\Tests\bin\x86\Release\Unittests.dll...
Loading C:\Proj\Tests\bin\x86\Release\Integrationtests.dll...
Starting execution...
Results Top Level Tests
------- ---------------
Failed Proj.IntegrationTest.IntegrationTest21
在执行中没有Unittests程序集,测试通过。
> mstest.exe /testcontainer:IntegrationTests.dll
Loading C:\Proj\Tests\bin\x86\Release\Integrationtests.dll...
Starting execution...
Results Top Level Tests
------- ---------------
Passed Proj.IntegrationTest.IntegrationTest21
我认为可能是在UnitTests dll上执行了[AssemblyInitialize]
操作,或者是Unittest.dll中的某种静态状态或公共依赖项在加载测试程序集时发生了修改。但我在Unittests.dll中找不到任何静态构造函数或程序集初始化操作。我怀疑在包含Unittests程序集时存在部署差异(例如,依赖程序集以不同的版本部署等),但我已经比较了通过/失败的部署目录,并且它们是二进制等效的。
因此,Unittests程序集的哪个部分会导致测试结果的不同? 从单元测试中,我每次移除一半的测试,直到将其缩小到Unit tests程序集中的一个源文件。除了测试类之外,还声明了一个扩展方法:
除了这个扩展类之外,Unittest程序集现在只包含一个虚拟测试类中的单个测试用例。只有当我声明了一个虚拟测试方法并且声明了扩展方法时,才会出现测试失败。我可以删除所有剩余的测试逻辑,直到Unittest dll成为一个仅包含这些内容的单个文件:
// DummyTest.cs in Unittests.dll
[TestClass]
public class DummyTest
{
[TestMethod]
public void TestNothing()
{
}
}
public static class IEnumerableExtension
{
public static IEnumerable<T> SymmetricDifference<T>(
this IEnumerable<T> @this,
IEnumerable<T> that)
{
return @this.Except(that).Concat(that.Except(@this));
}
}
如果测试方法或扩展类被移除,测试将通过。两者同时存在时,测试将失败。
在任何程序集中都没有调用扩展方法,并且在集成测试执行之前,Unittests程序集中也没有执行任何代码(据我所知)。
我确定集成测试足够复杂,即使是JIT优化上的差异也可能导致浮点数值的差异。这就是我看到的吗?
var1.SymmetricDifference(var2)
被替换为IEnumerableExtension.SymmetricDifference(var1, var2)
。 - Dominic Zukiewicz