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.

Dependency Injection in Unittests

In unterschiedlichen Szenarien sind oft unterschiedliche Implementierungen einer Schnittstelle erforderlich - gerade hierfür werden ja Schnittstellen verwendet: Zum Test einer Klasse, die beispielsweise einen Service verwendet, ist es wünschenswert, ein Testdouble (Mock, Stub, Fake, Spy o.ä.) zu verwenden. In einem anderen Ausführungskontext dagegen ist der Einsatz der echten Implementierung erforderlich.

Um nicht in diesen unterschiedlichen Ausführungskontexten manuell die Abhängigkeit zu injizieren oder unterschiedliche Anbindungen manuell zu implementieren, soll ein flexibler Mechanismus verwendet werden, der auch konfigurierbar ist. Dieses Muster beschreibt die Verwendung von DependencyInjection in Unittests.

Design

Für Unittests ist die manuelle Injektion der Testdoubles notwendig.

Implementierung

In diesem Beispiel ist eine Testklasse zu sehen (siehe Testklassen für Unittests erstellen). Das Fixture richtet die Umgebung ein und liefert das zu testende Objekt zurück. Dieser Test soll sicherstellen, dass beim Aufruf der LeiheAus()-Methode des Controllers die Update()-Methode des im Controller verwendeten Repositorys aufgerufen wird. Hier wird ein Mock und Stub als Testdouble verwendet.

internal sealed partial class BuecherAusleihenControllerTest
{
    [Test]
    public void LeiheAus_WhereBenutzerkontoHasNoAusleihen_ShouldSaveBenutzerkonto()
    {
        BuecherAusleihenController testObject = fixture.CreateTestObject();
        fixture.Mocks.BenutzerkontoRepositoryMock.Setup(x => x.QueryBy(fixture.Data.BenutzerSid))
           .Returns(new Benutzerkonto(Guid.NewGuid(), fixture.Data.BenutzerName, fixture.Data.BenutzerSid)); // Stub-Verhalten

        testObject.LeiheAus(fixture.Data.BenutzerSid, fixture.Data.BuecherIds);

        fixture.Mocks.BenutzerkontoRepositoryMock.Verify(x => x.Update(It.IsAny<Benutzerkonto>())); // Mock-Verhalten
    }
}

Im Fixture (siehe Fixture für Testklasse erstellen) wird ein Testdouble erzeugt und konfiguriert. Danach wird es per manueller Constructor Injection an das zu testende Objekt übergeben.

internal sealed partial class BuecherAusleihenControllerTest : IDisposable
{
    // ...
    private sealed class Mocks
    {
        // ...
        public Mock<IBuchRepository> BuchRepositoryMock { get; } = new Mock<IBuchRepository>();

        public BuecherAusleihenController CreateTestObject()
        {
            return new BuecherAusleihenController(
                BenutzerkontoRepositoryMock.Object,
                BuchRepositoryMock.Object,
                BuecherAusgeliehenEventServiceGatewayMock.Object);
        }
    }

    private sealed class TestData
    {
        public string BenutzerSid { get; } = "cals_super_sid";
        public string BenutzerName { get; } = "carstens konto";
        // ...
    }

    private sealed class Fixture : IDisposable
    {
        public Mocks Mocks { get; } = new Mocks();
        // ...
        public TestData Data { get; } = new TestData();

        public void Dispose()
        { }

        public BuecherAusleihenController CreateTestObject()
        {
            return Mocks.CreateTestObject();
        }
    }
}

Stärken und Schwächen

Stärken
  • Es werden die Testdoubles (Mocks, Stubs, etc.) direkt injiziert, was einfacher und verständlicher ist
  • Der DI-Container wird nicht direkt sichtbar im Produktivcode verwendet, was eine bessere Trennung von Technik und Fachlichkeit darstellt
  • Keine Create()-Methoden und somit weniger Code
  • Keine Reset()-Methode für Unittests notwendig
  • Anhand der Anzahl der Übergabeargumente im Konstruktor kann erkannt werden, dass zu viele Abhängigkeiten vorliegen, was ein schlechtes Design impliziert
  • Es wird ein Schnittstellen-orientiertes Design gefördert
Schwächen
  • Das Zusammenstellen für Unittests, die z.T. integrativ sind, ist schwer
  • Schlechtere Codenavigierbarkeit

Benutzte Muster

Cookie Consent mit Real Cookie Banner