运行所有单元测试时失败,但选择所有测试并运行时却没有失败

49
我遇到了一些奇怪的问题。如果我在“测试资源管理器”中点击“运行全部”,那么我的某个单元测试会失败,但是如果我选择所有测试并点击“运行所选测试”,则该单元测试将通过。
这个失败的测试引发了一个反射错误:System.Reflection.TargetException: Non-static method requires a target.,该类型被定义在我要测试的 dll 代码中。这个类似乎没有任何异常——dll 中定义的许多其他类都可以正常运行反射。下面是测试堆栈跟踪。
注意:这是一个复杂的测试——它从一个 .xlsx 文件中读取输入和期望的答案,使用来自 xlsx 的数据填充 LocalDb,在 LocalDB 中执行计算,然后将计算结果与期望值进行比较。但是,就像我说的,它可以工作,并且在运行所有测试(使用“全选”>“运行所选测试”)时也可以工作。
运行全部有什么不同?任何见解都会受到赞赏。
我尝试过干净和重建,但没有成功。捕获和记录反射错误表明每次我尝试访问类型上的任何属性时,GetValue 调用都会失败。但只有在“运行全部”时才会失败,并且只针对这一个类型?(如果我捕获错误,则所有其他类型上的所有 GetValues 都会成功。)
堆栈跟踪
Test Name:  IT_CheckCashOnly1DepositOutputValues
Test FullName:  Lib.AE.Tests.Integration.CalculationTests.IT_CheckCashOnly1DepositOutputValues
Test Source:    c:\netreturn.co.za\Main\NetReturn\Lib.AE.Tests\IntegrationTests\CalculationTest.cs : line 23
Test Outcome:   Failed
Test Duration:  0:00:00.1661906

Result Message: 
Test method Lib.AE.Tests.Integration.CalculationTests.IT_CheckCashOnly1DepositOutputValues threw exception: 
System.Reflection.TargetException: Non-static method requires a target.
Result StackTrace:  
at System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target)
at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index)
at Lib.AE.Xlsx.XlsxHelper.Compare[T](T expected, T calculated, ExcelWorksheet ws, Int32 r, Int32 colStart, Boolean& valid) in c:\netreturn.co.za\Main\NetReturn\Lib.AE\Xlsx\XlsxHelper.cs:line 101
at Lib.AE.Xlsx.XlsxWorkSheet_SharePNL.CompareXlsx(ExcelPackage pck, List`1 expectedXlsx, ValuationCalculation calc) in c:\netreturn.co.za\Main\NetReturn\Lib.AE\Xlsx\XlsxSharePNL.cs:line 143
at Lib.AE.Tests.Integration.CalculationTests.CheckCalculationResults(String xlsxDocToLoad, WorkSheets testingScenarios) in c:\netreturn.co.za\Main\NetReturn\Lib.AE.Tests\IntegrationTests\CalculationTest.cs:line 64
at Lib.AE.Tests.Integration.CalculationTests.IT_CheckCashOnly1DepositOutputValues() in c:\netreturn.co.za\Main\NetReturn\Lib.AE.Tests\IntegrationTests\CalculationTest.cs:line 23

解决方案

这个问题最终被证明是我的问题 - 我的单元测试与另一个单元测试共享了状态,而且是一个顺序问题。请注意,TestExplorer将以哪种顺序运行您的测试并不明显。我创建了一个新的UnitTestProject,其中包含2个UnitTest .cs文件和每个文件中的三个TestMethods,即:

UnitTest1.cs

    [TestMethod]
    public void ONE_AAA() {}

    [TestMethod]
    public void ONE_BBB() {}

    [TestMethod]
    public void ONE_CCC() {}

UnitTest2.cs

    [TestMethod]
    public void TWO_CCC() {}

    [TestMethod]
    public void TWO_BBB() {}

    [TestMethod]
    public void TWO_AAA() {}

然后通过两种方法运行这些测试,即(1)运行全部(2)选择全部并运行所选测试,并记录TestExplorer启动测试的顺序。对于“运行所选的测试”,结果相当令人费解:

-- Run All
2013-01-16 11:53:47.4062 INFO TestInitialize: ONE_AAA
2013-01-16 11:53:47.4122 INFO TestCleanup: ONE_AAA
2013-01-16 11:53:47.4122 INFO TestInitialize: ONE_BBB
2013-01-16 11:53:47.4122 INFO TestCleanup: ONE_BBB
2013-01-16 11:53:47.4122 INFO TestInitialize: ONE_CCC
2013-01-16 11:53:47.4282 INFO TestCleanup: ONE_CCC
2013-01-16 11:53:47.4282 INFO TestInitialize: TWO_CCC
2013-01-16 11:53:47.4282 INFO TestCleanup: TWO_CCC
2013-01-16 11:53:47.4282 INFO TestInitialize: TWO_BBB
2013-01-16 11:53:47.4282 INFO TestCleanup: TWO_BBB
2013-01-16 11:53:47.4282 INFO TestInitialize: TWO_AAA
2013-01-16 11:53:47.4282 INFO TestCleanup: TWO_AAA

-- Select All > Run Selected
2013-01-16 11:55:26.0139 INFO TestInitialize: TWO_BBB
2013-01-16 11:55:26.0139 INFO TestCleanup: TWO_BBB
2013-01-16 11:55:26.0249 INFO TestInitialize: ONE_BBB
2013-01-16 11:55:26.0249 INFO TestCleanup: ONE_BBB
2013-01-16 11:55:26.0249 INFO TestInitialize: TWO_AAA
2013-01-16 11:55:26.0249 INFO TestCleanup: TWO_AAA
2013-01-16 11:55:26.0249 INFO TestInitialize: TWO_CCC
2013-01-16 11:55:26.0249 INFO TestCleanup: TWO_CCC
2013-01-16 11:55:26.0249 INFO TestInitialize: ONE_CCC
2013-01-16 11:55:26.0249 INFO TestCleanup: ONE_CCC
2013-01-16 11:55:26.0249 INFO TestInitialize: ONE_AAA
2013-01-16 11:55:26.0249 INFO TestCleanup: ONE_AAA

1
请发布您的测试代码。 - Codeman
这太大了,无法上传——它是我的整个代码库(问题只发生在运行全部代码时)。即使是单个单元测试也很大。我正在尝试解决这个问题——例如,看看是否可以分离出一个小一些的单元测试项目,仍然表现出相同的行为,但可能需要一段时间。 - Ilan
我也遇到了这个问题。我意识到在一个类中有一个静态变量没有在每个[Test]之后被重置。 - dano
你是如何记录测试运行的顺序的? - TamaMcGlinn
5个回答

20

我过去在运行多个单元测试时曾遇到问题,因为测试运行的顺序可能不是测试类中声明的顺序,而实际上可能按照测试方法名称的顺序进行。例如,如果我有:

[Test]
public void PreviousTest()
{
}

[Test]
public void LaterTest()
{
}

按字母顺序排序,因此名称在PreviousTest之前的LaterTest将被首先运行。

如果所有测试都是独立的,则这并不重要,但是如果它们修改共享资源,则您可能会获得意外行为,如果您预期由于声明第二而LaterTest的更改不会对PreviousTest产生任何影响。


1
谢谢Dean,事实上这确实是一个顺序问题。选择测试并“运行所选”可以使用与“运行全部”不同的顺序。反射异常是一个误导(由于顺序问题和失败测试与先前测试之间的依赖关系)。我将在我的问题中写一些额外的注释,以帮助其他人解决此类问题。 - Ilan

10
确保您的成员在设置范围内初始化,否则它们可能会被重用。
[TestFixture]
public class RegistrationInteractorTests
{
    private  IRegistrationService _registrationService;
    private  IRegistrationValidationService _registrationValidationService;
  ......

    [SetUp]
    public void Init()
    {
        _registrationService = A.Fake<IRegistrationService>();
        _registrationValidationService = A.Fake<IRegistrationValidationService>();
    }

....

}

// The wrong way would be like this:

 [TestFixture]
    public class RegistrationInteractorTests
    {
        protected readonly IRegistrationService _registrationService = A.Fake<IRegistrationService>();

        protected readonly IRegistrationValidationService _registrationValidationService =
            A.Fake<IRegistrationValidationService>();



        [SetUp]
        public void Init()
        {

        }

2
我们已经尝试调查这个问题一个星期了。请确保所有的测试项目都使用相同版本的DLL,特别是Nuget包。
问题在于,当你执行“Run All”时,所有测试项目中使用的库都会放置在一个文件夹中(类似于MyApp\TestResults\Deploy_user1 2018-11-20 15_52_15\Out),由于这些DLL库冲突导致问题出现。

0
正如其他人所说,这通常是因为测试之间存在一些共享的状态/数据导致的。
在我们的情况下,我们给所有的内存数据库都取了相同的名称,解决方法就是给每个数据库赋予一个唯一的名称。
var options = new DbContextOptionsBuilder<SomeDataContext>()
    .UseInMemoryDatabase(databaseName: $"TestDb_{Guid.NewGuid()}")
    .Options;

_dbContext = new SomeDataContext(options);

0

[TestInitialize] 方法在每个单独的测试运行之前运行。请从中删除任何与单个测试相关的代码,并将其放置在各自的测试中。


我认为这不是与多个测试相关的问题(例如,另一个单元测试更改状态导致运行失败的测试)——因为当我运行多个测试时它可以工作。它可能与“Run All”和反射有关。你知道“Run All”有何不同吗?嗯...实际上,我可以更快地记录单元测试运行的顺序,以验证“选择所有> 运行所选内容”的运行顺序是否与“运行全部”相同。在尝试引起更小的单元测试中出现相同的行为之前,我会先尝试这样做(如我上面的评论所述)。 - Ilan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接