【问题标题】:FakeItEasy sometimes fails to create a fake when tests are run in parallel当测试并行运行时,FakeItEasy 有时无法创建假
【发布时间】:2016-05-31 02:00:49
【问题描述】:

使用 FakeItEasy 的单元测试在尝试伪造简单的接口时随机失败。它偶尔会在不同的测试中出现,并且不稳定。

这是我需要伪造的示例界面:

public interface IJobSuiteFilterApplier
{
     JobSuiteDto FilterJobSuites(JobSuiteDto jobSuiteDto, JobSuiteFilter jobSuiteFilter);
}

这是一段创建假的代码,有时会失败:

var jobSuiteFilterApplier = A.Fake<IJobSuiteFilterApplier>(x => x.Strict());

这里是异常详情:

FakeItEasy.Core.FakeCreationException: 
  Failed to create fake of type "QS.TestShell.Server.ExecutionPlanner.Queries.IExecutionPlannerQueryService".

  Below is a list of reasons for failure per attempted constructor:
    No constructor arguments failed:
      No usable default constructor was found on the type QS.TestShell.Server.ExecutionPlanner.Queries.IExecutionPlannerQueryService.
      An exception was caught during this call. Its message was:
      Collection was modified; enumeration operation may not execute.


    at FakeItEasy.Core.DefaultExceptionThrower.ThrowFailedToGenerateProxyWithResolvedConstructors(Type typeOfFake, String reasonForFailureOfUnspecifiedConstructor, IEnumerable`1 resolvedConstructors)
   at FakeItEasy.Creation.FakeObjectCreator.TryCreateFakeWithDummyArgumentsForConstructor(Type typeOfFake, FakeOptions fakeOptions, IDummyValueCreationSession session, String failReasonForDefaultConstructor, Boolean throwOnFailure)
   at FakeItEasy.Creation.FakeObjectCreator.CreateFake(Type typeOfFake, FakeOptions fakeOptions, IDummyValueCreationSession session, Boolean throwOnFailure)
   at FakeItEasy.Creation.DefaultFakeAndDummyManager.CreateFake(Type typeOfFake, FakeOptions options)
   at FakeItEasy.Creation.DefaultFakeCreatorFacade.CreateFake[T](Action`1 options)
   at FakeItEasy.A.Fake[T](Action`1 options)

当我添加以下内容时,测试通过了,但是我需要将其添加到所有虚假创作中看起来很奇怪:

var jobSuiteFilterApplier = A.Fake<IJobSuiteFilterApplier>(x => x.Strict().Synchronized());



public class CallSynchronizer : IInterceptionListener
{
    private static readonly object SynchronizationLock = new object();

    public void OnBeforeCallIntercepted(IFakeObjectCall interceptedCall)
    {
        Monitor.Enter(SynchronizationLock);
    }

    public void OnAfterCallIntercepted(ICompletedFakeObjectCall interceptedCall, IFakeObjectCallRule ruleThatWasApplied)
    {
        Monitor.Exit(SynchronizationLock);
    }
}

public static class MyPersonalFakeExtensions
{
    public static IFakeOptionsBuilder<T> Synchronized<T>(this IFakeOptionsBuilder<T> builder)
    {
        return builder.OnFakeCreated(fake => Fake.GetFakeManager(fake).AddInterceptionListener(new CallSynchronizer()));

    }
}

更新:我正在开发人员机器上使用 ReSharper 测试运行程序运行测试,并在构建服务器上使用 mstext.exe。并发设置允许一次运行多个测试。

【问题讨论】:

  • 你使用什么单元测试框架?您是在并行运行单元测试还是在多个线程上创建伪造品?那似乎是open issue
  • 抱歉没有提及。我正在使用 FakeItEasy。我正在开发人员机器上使用 ReSharper 测试运行程序并在构建服务器上使用 mstext.exe 运行测试。

标签: c# unit-testing mocking fakeiteasy


【解决方案1】:

更新:FakeItEasy 2.0.0 大大改进了对并行运行测试的支持。试试看。

正如 mike z 所说:FakeItEasy 目前不支持多线程测试。这是因为并非所有内部结构都是线程安全的,而且要使其完全线程安全并不容易。有一个未解决的问题,number 60,用于支持多线程测试执行。

目前,您提供的解决方案是实现此目的的唯一方法,正如最初在此处 http://hmemcpy.com/2012/12/running-multithreaded-unit-tests-with-fakeiteasy/ 解释的那样。

无法为所有假货全局添加拦截侦听器,但是您可以使用FakeConfigurator&lt;T&gt; 类自动执行此行为每个类型,因此您可以选择包含每个假货type,类如

public class SomeTypeSynchronousConfigurator : FakeConfigurator<ISomeType>
{
    public override void ConfigureFake(ISomeType fakeObject)
    {
        Fake.GetFakeManager(fakeObject)
                 .AddInterceptionListener(new CallSynchronizer());
    }
}

FakeItEasy 将 discover the class 并且(在这种情况下)ISomeType 的每个新伪造品都将应用同步器 - 您可以像 var fake = A.Fake&lt;ISomeType&gt;(); 一样制作伪造品。

【讨论】:

  • +1 Philipp 为答案,+1 Blair 为精美装饰!
猜你喜欢
  • 1970-01-01
  • 2011-02-10
  • 1970-01-01
  • 1970-01-01
  • 2016-12-12
  • 2013-10-10
  • 2016-09-06
  • 1970-01-01
  • 2017-12-10
相关资源
最近更新 更多