Work in Progress
Diese Seite ist aktuell im Review! Die Seite wurde noch nicht qualitätsgesichert und kann Fehler enthalten.
Die verlinkten Seiten sind ggf. nur für Schleupen-Mitarbeiter sichtbar.

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

Benutzte Muster

Cookie Consent mit Real Cookie Banner