扩展方法存在但未调用时代码执行的差异

时间:2014-02-17 09:53:35

标签: c# extension-methods mstest jit

TL; DR,问题:

对于代码执行有什么影响可以在.NET中存在扩展方法(例如JIT /优化)?

背景

我在MSTest中遇到测试失败,这取决于是否还测试了看似无关的程序集。

我注意到测试失败并且偶然发现只有在加载了另一个测试组件时才会发生故障。在Unittests和Integration测试程序集上运行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

在执行过程中没有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中找不到任何静态构造函数和程序集init。当包含Unittests程序集时,我怀疑部署差异(依赖程序集部署在不同版本中等),但我比较了传递/失败的部署目录,它们是二进制等效的。

那么Unittests组件的哪个部分导致了测试差异? 从单元测试中我一次删除了一半的测试,直到我将它钻到Unit测试程序集中的源文件中。与测试类一起,声明了一个扩展方法:

除了这个扩展类之外,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差异会导致差异,例如:在浮点。这就是我所看到的吗?

3 个答案:

答案 0 :(得分:1)

可能是由于类型加载错误而导致问题。

当CLR运行时加载类或方法时,它总是检查这些项中使用的所有类型。是否实际调用了类型/方法并不重要。重要的是宣言的事实。返回到您的示例,扩展方法SymmetricDifference声明它使用System.Core程序集中的ExceptConcat方法。

从System.Core程序集加载类型System.Linq.Enumerable期间发生错误会发生什么。

这种行为的原因可能有所不同。要采取的第一步是记录测试失败时的确切异常。

答案 1 :(得分:0)

我确实找到了这个:https://connect.microsoft.com/VisualStudio/feedback/details/792429/some-unit-tests-fail-with-visual-studio-2012-agent-update3-on-windowsxp-sp3

显然错误的框架是错误的。在执行测试时,在命令行上传递/ noisolation被称为解决方法。希望这能解决您的问题。

答案 2 :(得分:0)

两个想法......

  1. 浮点数学和比较实际上可以在有/无优化的情况下给出不同的结果。至少他们可以in C++他们也可以,而且肯定会在不同的CPU架构上有所不同。一般来说,基于浮点比较来评估通过/失败的单元测试听起来是个坏主意。例如,运行x64窗口的朋友在成功时无法通过单元测试。

  2. 此扩展方法问题是一个疯狂的追逐。那么,如果你删除它会神秘地起作用呢?优化的代码有时会发疯,最终问题是你的浮点数与未经优化的时候不同。如果差异是不可避免的,请添加epsilon,否则在计算方式的每一步都记录其值并跟踪它。