2014-10-22 33 views
5

使用FakeItEasy的單元測試在嘗試僞造簡單的intefrace時會隨機失敗。偶爾出現在不同的測試中,並且不穩定。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) 

當我添加以下內容時,測試通過,但是看起來很奇怪,我需要將它添加到所有fa柯創作:

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。併發設置允許一次運行多個測試。

+0

你使用什麼單元測試框架?你是在並行運行單元測試還是在多線程上創建假貨?這似乎是一個[公開問題](https://github.com/FakeItEasy/FakeItEasy/issues/60)。 – 2014-10-22 07:10:21

+0

抱歉沒有提及它。我正在使用FakeItEasy。我正在開發人員機器上使用ReSharper測試運行器運行測試,並在構建服務器上使用mstext.exe。 – 2014-10-22 07:15:53

回答

10

更新:FakeItEasy 2.0.0大幅改進了對並行測試的支持。嘗試一下。

作爲mike z提到:FakeItEasy目前不支持多線程測試。這是因爲並非所有內部部件都是線程安全的,並且使它完全線程安全並不容易。有一個開放的問題,number 60,用於支持多線程測試執行。

現在您提供的解決方案是實現此目的的唯一方法,正如此處最初解釋http://hmemcpy.com/2012/12/running-multithreaded-unit-tests-with-fakeiteasy/

有沒有辦法在全球範圍內攔截監聽器添加到所有的假貨,但是你可以使用FakeConfigurator<T>類每型自動化此行爲,所以你可以選擇將包括每一個僞造的類型,一類如

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<ISomeType>();

+0

謝謝你的回答。 – 2014-10-22 09:34:05

+0

+1菲利普的答案和+1布萊爾的漂亮點綴! – 2014-10-23 05:28:06