2015-11-10 18 views
5

Biorąc pod uwagę dwie klasy:Dostosowywanie AutoFixure użyciu FromSeed Powoduje Wyjątek

class Foo 
{ 
    ... 
} 

class Bar 
{ 
    public Foo FooBar { get; set; } 
} 

Mam utworzone następujący test:

void Test() 
{ 
    var fixture = new Fixture(); 

    fixture.Customize<Foo>(x => x.FromSeed(TestFooFactory)); 

    var fooWithoutSeed = fixture.Create<Foo>(); 
    var fooWithSeed = fixture.Create<Foo>(new Foo()); 

    var bar = fixture.Create<Bar>(); //error occurs here 
} 

Foo TestFooFactory(Foo seed) 
{ 
    //do something with seed... 

    return new Foo(); 
} 

mogę utworzyć Foo obiekty bezpośrednio i bez wartości materiału siewnego bez problemu . Ale gdy próbuję utworzyć Bar obiekt, który ma właściwość Foo, otrzymuję ObjectCreationException:

W ozdobione ISpecimenBuilder nie może utworzyć próbki w oparciu o wniosek: Foo. Może się to zdarzyć, jeśli żądanie reprezentuje interfejs lub klasę abstrakcji; w takim przypadku zarejestruj moduł ISpecimenBuilder, który może tworzyć próbki na podstawie żądania. Jeśli dzieje się to w mocno napisanym wyrażeniu kompilacji, spróbuj podać fabrykę, używając jednej z metod IFactoryComposer.

będę oczekiwać TestFooFactory aby przyjął wartość null nasion podczas tworzenia Bar, podobnie jak wtedy, gdy tworzone Foo bez wartości nasion. Czy robię coś nie tak, czy może to błąd?

W moim realnym scenariuszu, chcę dostosować sposób, w jaki AutoFixture używałby zaimportowanych wartości dla pewnych obiektów, kiedy przekazuję zalegalizowane wartości, ale nadal chcę, aby AutoFixture było domyślnym normalnym zachowaniem, jeśli nie podano nasion.

+3

Crossposted do GitHub: https://github.com/AutoFixture/AutoFixture/issues/467 –

Odpowiedz

5

Sposób dostosowywania Fixture do używania wartości początkowych is correct.

Zachowanie, które widzisz, jest konsekwencją modyfikacji w kodzie AutoFixture modyfikowanej przez dostosowywanie FromSeed. Jeśli chcesz przeczytać szczegółowe informacje, opisałem je jako here.

Jako obejście można użyć konstruktora niestandardowy wzór dla wysianych wniosków jak ten:

public class RelaxedSeededFactory<T> : ISpecimenBuilder 
{ 
    private readonly Func<T, T> create; 

    public RelaxedSeededFactory(Func<T, T> factory) 
    { 
     this.create = factory; 
    } 

    public object Create(object request, ISpecimenContext context) 
    { 
     if (request != null && request.Equals(typeof(T))) 
     { 
      return this.create(default(T)); 
     } 

     var seededRequest = request as SeededRequest; 

     if (seededRequest == null) 
     { 
      return new NoSpecimen(request); 
     } 

     if (!seededRequest.Request.Equals(typeof(T))) 
     { 
      return new NoSpecimen(request); 
     } 

     if ((seededRequest.Seed != null) 
      && !(seededRequest.Seed is T)) 
     { 
      return new NoSpecimen(request); 
     } 

     var seed = (T)seededRequest.Seed; 

     return this.create(seed); 
    } 
} 

Następnie można go używać do tworzenia obiektów typu Foo tak:

fixture.Customize<Foo>(c => c.FromFactory(
    new RelaxedSeededFactory<Foo>(TestFooFactory))); 

Dostosowanie to przejdzie default(Foo) - czyli null - jako materiał siewny do funkcji fabrycznej TestFooFactory podczas wypełniania właściwości typu Foo.

+0

To działa jak urok! Dzięki za obejście. –

+2

Zaktualizowałem niestandardowy builder "RelaxedSeededFactory" z metodą _better_. Zamiast obsługiwać wszystkie żądania, obsłuży tylko obsadzone i nierozsiete żądania dla 'T'. –

+1

Podobnie jak aktualizacja, AutoFixture 3.36.12 rozwiązuje ten problem: https://github.com/AutoFixture/AutoFixture/commit/c7ce7d239a26fd7aef8c464ba214c366681d0886. Dzięki @Enrico! –