【发布时间】:2014-02-17 09:53:35
【问题描述】:
TL;DR,问题:
.NET 中扩展方法的存在会对代码的执行产生什么影响(例如 JIT/优化)?
背景
我在 MSTest 中遇到测试失败,这取决于是否还测试了看似不相关的程序集。
我注意到测试失败,并且偶然注意到只有在加载另一个测试程序集时才会发生失败。在单元测试和集成测试程序集上运行 mstest 将开始执行集成测试,并在 4.5 CLR 下的第 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
如果没有执行单元测试程序集,则测试通过。
> 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 程序集的哪一部分导致了测试差异? 从单元测试中,我一次删除了一半的测试,直到我将其深入到单元测试程序集中的源文件中。与测试类一起声明了一个扩展方法:
除了这个扩展类之外,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 差异可能会导致差异,例如在浮点数。这就是我所看到的吗?
【问题讨论】:
-
我不会说 Extensions 类会导致任何问题。它最终是语法糖,在编译时被替换:
var1.SymmetricDifference(var2)被替换为IEnumerableExtension.SymmetricDifference(var1, var2)。 -
确切的错误信息或异常是什么?事件日志中有任何内容吗?如果您在测试中执行 Assert,报告失败的测试的实际代码是什么。
-
事件日志中没有任何内容,唯一的例外是AssertFailed,它来自于测试中浮点结果的比较。我无法对问题进行最低限度的重现。
-
如果在调试模式下编译和运行这些测试会发生什么?导致 Assert 失败的比较值是什么?您是否尝试过将
SymmetricDifference替换为简单的return that或throw new NotImplementedException? -
这个问题只出现在优化的构建中,只要加载了它的程序集,扩展方法的内容似乎并不重要。它永远不会被测试调用。
标签: c# extension-methods mstest jit