2014-02-17 21 views
12

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 

我想这一定是在单元测试的dll正在执行的[AssemblyInitialize]东西,或者也许是某种在Unittest.dll或公共依赖静态的加载测试组件时被修改。我在Unittests.dll中找不到任何静态构造函数或组装体init。我怀疑包含Unittests程序集时的部署差异(以不同版本部署的相关程序集等),但我比较了通过/失败的部署目录,并且它们是二进制等价的。

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

除了此扩展类,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在优化方面的差异可能会导致不同,例如在浮点。那是我所看到的吗?

+0

我不会说扩展类导致任何问题。它最终是语法糖,在编译时被替换:'var1.SymmetricDifference(var2)'被替换为'IEnumerableExtension.SymmetricDifference(var1,var2)'。 –

+0

什么是确切的错误信息或异常?事件日志中是否有任何内容?如果您在测试中做了断言,那么报告失败的测试的实际代码是什么。 – CodeCowboyOrg

+0

事件日志中没有任何内容,唯一的例外是AssertFailed,它来自测试中浮点结果的比较。我无法制造出这个问题的最小代价。 –

回答

1

可能由于类型加载错误而发生此问题。

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

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

该行为的原因可能会有所不同。要采取的第一步是记录您在测试失败时获得的确切例外。

+0

感谢您的建议,我没有看到任何异常,但(没有TypeLoaderExceptions等)。如果需要,我可以使用扩展方法,并按预期工作。测试失败看起来像是一个“正常”的测试失败,可能是浮点差异导致的比较差异。 –

+0

mstest.exe可以生成.trx文件。然后,您可以在测试失败后打开生成的.trx文件,查看您得到的确切错误以及在哪里。 – ogggre

+0

这是一个简单的“正常”测试断言失败,如“预计4苹果,但发现3”。测试结果中没有例外。 –

0

两个想法...

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

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

+0

我知道优化会导致浮点行为的差异,特别是在x86上(在x64中抖动倾向于使用SSE的情况更少)。我的问题不是那么多“我该如何解决这个问题”(我可以避免测试),而是我很好奇为什么看起来优化是在不同的情况下做出的方法。我可以确定与扩展名/扩展名不同的地方,问题是:为什么? –