Fixture für Fachdaten erstellen
Dieses Muster soll den Entwurf von Fixture-Klassen zur Bereitstellung von fachlichen Aggregaten für Tests dokumentieren. Alle fachlichen Tests - von Unittests zu Repository.ConnectionTests über ComponentTests mit DB-Zugriff zu weiteren höherwertigen automatisierten in C# geschriebenen Tests - benötigen fachliche Aggregate für Tests: Diese werden in Form eine sogenannten Fachdaten-Fixtures implementiert.
Design
Pro Aggregate wird ein Fachdaten-Fixture (siehe Objektpersistierung mit NHibernate) erstellt, das die Fachdaten zur Laufzeit erstellt, die für den Test benötigt werden.
Das Fixture wird in einer Assembly namens <Produkt>.Domain.Testing
bereitgestellt.
Implementierung
Beim Fixture zur Datenbereitstellung wird folgende Namenskonvention verwendet: [AggregateRootName]Fixture
Zur einfacheren Testdatenerstellung wird die Bibliothek AutoFixture verwendet. Zur Implementierung wird das Erbauer-Muster der GoF verwendet.
Beispiel
Hier ist Buch
das AggregateRoot.
public sealed class BuchFixture { private readonly IFixture fixture; private Buch buch; private Inhaltsangabe inhaltsangabe; private ISet<Seite> seiteList; private Genre genre; private Sprache sprache; private Raumnummer raumnummer; private string zusammenfassung; private Isbn isbn; public BuchFixture() { fixture = Fixture(); fixture.Customize<BuchId>(c => c.FromFactory(() => BuchId(GuidGenerator.Create()))); fixture.Customize<Isbn>(c => c.FromFactory(() => Isbn(IsbnNummer))); fixture.Customize<Inhaltsangabe>(i => i.Without(x => x.ParentId)); // ParentIds sind Objekte, erlauben aber nur Guids fixture.Customize<Seite>(i => i.Without(x => x.ParentId)); zusammenfassung = fixture.Create("Zusammenfassung"); } [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Ist hier OK.")] public BuchId[] BuchIds => fixture.CreateMany<BuchId>().ToArray(); private string IsbnNummer => fixture .Build<string>() .FromFactory(() => SpecimenContext(fixture).Resolve(RegularExpressionRequest(Isbn.ValidNummerRegexPattern)).ToString()) .Create(); public BuchFixture NewBuch(string titelprefix = null) { buch = fixture.Create<Buch>(); IBuchData buchData = buch; buchData.Zusammenfassung = zusammenfassung; if (titelprefix != null) { buchData.Titel = titelprefix + buchData.Titel; } WithIsbn(); return this; } public BuchFixture WithSeite() { Seite seite = fixture.Create<Seite>(); ISeiteData seiteData = seite; seiteData.Seitenzahl = 1; seiteData.ParentId = inhaltsangabe.Id; seiteList = HashSet<Seite> { seite }; return this; } public BuchFixture WithInhaltsangabe() { inhaltsangabe = fixture.Create<Inhaltsangabe>(); ((IInhaltsangabeData)inhaltsangabe).ParentId = buch.Id; return this; } public BuchFixture WithGenre() { genre = Genre.Krimi; return this; } public BuchFixture WithSprache() { sprache = Sprache("Deutsch"); return this; } public BuchFixture WithZusammenfassung(string neueZusammenfassung) { zusammenfassung = neueZusammenfassung; return this; } public BuchFixture WithRaumnummer() { IFixture raumnummerFixture = Fixture(); string raumnummerString = raumnummerFixture.Build<string>() .FromFactory(() => SpecimenContext(raumnummerFixture).Resolve(RegularExpressionRequest(Raumnummer.ValidNummerRegexPattern)).ToString()) .Create(); raumnummer = Raumnummer(raumnummerString); return this; } public BuchFixture WithIsbn(Isbn newIsbn) { isbn = newIsbn; return this; } private BuchFixture WithIsbn() { IFixture isbnFixture = Fixture(); string isbnString = isbnFixture .Build<string>() .FromFactory(() => SpecimenContext(isbnFixture).Resolve(RegularExpressionRequest(Isbn.ValidNummerRegexPattern)).ToString()) .Create(); WithIsbn(Isbn(isbnString)); return this; } public Buch Build() { IBuchData buchData = buch; if (buchData != null) { buchData.Inhaltsangabe = inhaltsangabe; buchData.Zusammenfassung = zusammenfassung; buchData.Isbn = isbn; // ... IInhaltsangabeData inhaltsangabeData = inhaltsangabe; if (inhaltsangabeData != null) { inhaltsangabeData.Seiten = seiteList; } } buch.ValidateRegardingPersistence().ThrowIfInvalid(); return buch; } }
Beachte, dass die Validierung sicherstellt, dass ein korrektes Objekt erstellt wird.
Stärken und Schwächen
Stärken
- Wiederverwendung von bereits vorhandenen Fixtures
- Es ist einfacher, voneinander unabhängige Tests zu schreiben
- Größere Übersichtlichkeit
Schwächen
- Erhöhter Implementierungsaufwand