2014-10-22 5 views
5

I test di unità che utilizzano FakeItEasy casualmente falliscono quando si tenta di simulare un semplice intefrace. Occasionalmente si verifica in diversi test e non è stabile.FakeItEasy a volte non riesce a creare un falso quando i test vengono eseguiti in parallelo

Ecco un'interfaccia di esempio ho bisogno di fingere:

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

Ecco pezzo di codice che crea il falso e non riesce a volte:

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

Ecco i dettagli di eccezione:

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) 

Quando aggiungo quanto segue, i test passano, ma sembra strano che io debba aggiungerlo a tutti creazione ke:

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

    } 
} 

Aggiornamento: Sono in esecuzione i test utilizzando ReSharper prova corridore sulla macchina di sviluppo e l'utilizzo di mstext.exe sul server di build. Le impostazioni di concorrenza consentono di eseguire più test contemporaneamente.

+0

Quale framework di test unità stai usando? Stai eseguendo i test unitari in parallelo o creando falsi su più thread? Sembra essere un [problema aperto] (https://github.com/FakeItEasy/FakeItEasy/issues/60). –

+0

Ci scusiamo per non averlo menzionato. Sto usando FakeItEasy. Sto eseguendo i test utilizzando ReSharper test runner sulla macchina dello sviluppatore e utilizzando mstext.exe sul build server. –

risposta

10

Aggiornamento: FakeItEasy 2.0.0 ha migliorato drasticamente il supporto per i test eseguiti in parallelo. Provalo.

come mike z menzionato: FakeItEasy al momento non supporta i test multi-thread. Questo perché non tutti gli interni sono sicuri per i thread e non è facile renderlo completamente sicuro per i thread. C'è un problema aperto, number 60, per supportare l'esecuzione di test multi-thread.

Per ora la soluzione che hai fornito è l'unico modo per raggiungere questo obiettivo, come spiegato in origine qui http://hmemcpy.com/2012/12/running-multithreaded-unit-tests-with-fakeiteasy/.

Non v'è alcun modo per globalmente aggiungere un listener di intercettazione a tutti i falsi, tuttavia è possibile utilizzare la classe FakeConfigurator<T> per automatizzare questo comportamento per tipo di, quindi si potrebbe optare per includere, per ogni tipo, una classe falsificate come

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

FakeItEasy sarà discover the class e ogni nuovo falso di (in questo caso) ISomeType avrà il sincronizzatore applicato-si può fare il falso proprio come var fake = A.Fake<ISomeType>();.

+0

Grazie per la risposta. –

+0

+1 Philipp per la risposta e +1 Blair per il bel abbellimento! –