O bibliotece AutoFixture wspominałem już ostatnio we wpisie Lightning talk – Autofixture. Biblioteka ta pomaga nam w tworzeniu obiektów w trakcie fazy [mark]Arrange[/mark] w testach jednostkowych. Dzięki niej możemy skupić się na tym co rzeczywiście ma zostać przetestowane, a nie na tworzeniu obiektów, które są potrzebne do przeprowadzenia testu. Dodatkowo chroni ona nas przed niepotrzebną modyfikacją testów w momencie zmiany wykorzystywanych w testach obiektów.
Ma ona drobną wadę, w zależności od obiektów, które tworzymy za jej pomocą może ona spowodować znaczące wydłużenie trwania testów jednostkowych. W szczególności, gdy za jej pomocą tworzymy obiekty związane z EntityFrameworkiem.
Oczywiście jest na ten problem rozwiązanie, którym chciałbym się z Wami podzielić. Musimy napisać kod, który spowoduje, że AutoFixture nie będzie generował obiektów, które oznaczone są jako virtual w klasach. Aby to zrobić należy po pierwsze zaimplementować własnego SpecimenBuilder-a:
public class IgnoreVirtualMembersSpecimenBuilder : ISpecimenBuilder { public object Create(object request, ISpecimenContext context) { var propertyInfo = request as PropertyInfo; if (propertyInfo == null) { return new NoSpecimen(); } if (propertyInfo.GetGetMethod().IsVirtual) { return null; } return new NoSpecimen(); } }
A następnie odpowiednio skonfigurować AutoFixture:
var fixture = new Fixture(); fixture.Customizations.Add(new IgnoreVirtualMembersSpecimenBuilder());
Rozwiązanie to działa w większości przypadków. Należy tylko pamiętać, że w momencie implementacji interfejsu przez jakąś klasę CLR domyślnie oznaczy elementy wskazane w interfejsie jako virtual i przez to nie zostaną one wygenerowane przez tak skonfigurowany Fixture. Patrząc na poniższy przykład:
public interface IUser { string Name { get; set; } string Surname { get; set; } } public class User : IUser { public string Name { get; set; } public string Surname { get; set; } }
Należy zauważyć, że zarówno do właściwości Name, jak i Surname zostanie przypisana wartość null. Zobaczcie sami i spójrzcie na poniższy test jednostkowy:
[Fact] public void IssueWithClassThatImplementsInterface() { // Arrange var fixture = new Fixture(); fixture.Customizations.Add(new IgnoreVirtualMembersSpecimenBuilder()); // Act var user = fixture.Create<User>(); // Assert Assert.Null(user.Name); Assert.Null(user.Surname); }
Opisane rozwiązanie pozwoli zaoszczędzić nam sporo czasu w przypadku budowania złożonych obiektów, w szczególności tych powiązanych z EntityFrameworkiem.
Thank you!! This saved me!