Dirk Richter
Software-Entwicklung und Architektur
Probleme bei Unittests mit LoD und DIP
Wenn das Law of Demeter (LoD) und das Dependency Inversion Principle (DIP) verletzte wird, ist es schwierig entkoppelte Komponententests zu schreiben: Das Ziel ist, LoD und DIP schrittweise einzuführen, um saubere Komponententests zu ermöglichen. Suche nach Code-Stellen mit langen Methodenketten und direkten Instanziierungen/Verwendungen von Implementierungen. Erstelle in Klassen Methoden, die die Ketten abkürzen: Jetzt kann man Ersetze konkrete Typen durch Interfaces, z.B.: Dadurch kann man nun in Tests einfach ein Mock-Repository verwenden. Verwende Dependency Injection Frameworks (wie Microsoft.Extensions.DependencyInjection) oder injiziere Abhängigkeiten über den Konstruktor. So ist der Code testbar und flexibel. Durch die Einführung von Law of Demeter und Dependency Inversion Principle wird Code modularer, die Komponenten werden unabhängiger und die Unittests werden einfacher, schneller und zuverlässiger. Man kann so systematisch von schwergewichtigen Systemtests zu zielgerichteten Komponententests wechseln.
Inhaltsverzeichnis
Einleitung
order.customer.address.city). Dadurch hängen Komponenten eng zusammen und sind schwer unabhängig zu testen.
Iteratives Refaktoring zur Verbesserung
1. Identifizieren von „Trainwrecks“ und direkten Abhängigkeiten
1var city = order.customer.address.city; // LoD verletzt
2var repo = new OrderRepository(); // DIP verletzt
2. Fassaden und Methoden zur Kapselung einführen (LoD)
1public class Order {
2 public Customer Customer { get; set; }
3 public string GetCustomerCity() => Customer?.GetCity();
4}
5
6public class Customer {
7 public Address Address { get; set; }
8 public string GetCity() => Address?.City;
9}
order.GetCustomerCity() statt einer langen Kette verwenden.
3. Abhängigkeiten mit Interfaces abstrahieren (DIP)
1public interface IOrderRepository {
2 Order GetOrder(int id);
3}
4
5public class OrderService {
6 private readonly IOrderRepository _orderRepo;
7 public OrderService(IOrderRepository orderRepo) {
8 _orderRepo = orderRepo;
9 }
10}
4. Dependency Injection verwenden
1// NUnit Test
2[Test]
3public void GetOrder_ReturnsOrder() {
4 var repoMock = new Mock<IOrderRepository>();
5 repoMock.Setup(r => r.GetOrder(1)).Returns(new Order { /* ... */ });
6 var service = new OrderService(repoMock.Object);
7
8 var result = service.GetOrder(1);
9
10 Assert.IsNotNull(result);
11}
5. Iterativ vorgehen
6. Feedback und weitere Schritte
Zusammenfassung