Stubbing właściwości z prywatnymi ustawiaczami do testów

10

Mamy przedmiot

public class MyObject{
    protected MyObject(){}

    public string Property1 {get;private set;}
    public string Property2 {get;private set;}
    public string Property3 {get;private set;}
    public string Property4 {get;private set;}
    public string Property5 {get;private set;}
    public string Property6 {get;private set;}
    public string Property7 {get;private set;}
    public string Property8 {get;private set;}
    public string Property9 {get;private set;}
    public string Property10 {get;private set;}
}

W naszym kodzie produkcyjnym wypełniamy ten obiekt za pomocą automappera. Może uzyskać dostęp do właściwości i ustawić je poprawnie.

Teraz, gdy chcemy przetestować tę klasę w przyszłym potoku, nie jest możliwe wypełnienie właściwości wartościami zastępczymi (do przetestowania).

Dostępnych jest kilka opcji.

  • Niestandardowe konstruktory do akceptowania parametrów wymaganych do testów i ustawiania właściwości, obecnie wymagane są 3 konstruktory. Nie jest to czyste, ponieważ konstruktory nie obsługują żadnych funkcji biznesowych.

  • Ustaw właściwości wirtualnie, aby klasa mogła zostać skrócona. Ale zaznaczenie właściwości wirtualnych nie zapewnia żadnej wartości biznesowej i nie zanieczyszcza mojej klasy.

  • Dodaj konstruktor obiektów do klasy, aby wewnętrznie skonstruować obiekt. Ponownie brak wartości dodanej dla biznesu. Być może nieco czystszy, ale wciąż dużo niepotrzebnego kodu w obiektach domeny.

Wszelkie sugestie, porady lub alternatywne opcje tutaj?

JMan
źródło

Odpowiedzi:

8

Masz wiele opcji.

  • Śmiało, użyj niestandardowych konstruktorów, właściwości wirtualnych lub konstruktora obiektów. Uzasadnieniem tego jest to, że obiekt powinien stać sam, a nie zależeć od jakiejś magii, takiej jak automapper. Klasa, która jest całkowicie bezużyteczna, chyba że dzieje się coś magicznego, nie jest zbyt dobrze przemyślaną klasą. „Wartość biznesowa” nie jest jedynym wyznacznikiem dobrego projektu.

  • Uwzględnij automapper w procesie testowania. Niektórzy powiedzą, że to już nie jest test jednostkowy. Nie ważne. Testy jednostkowe nie są jedynym rodzajem testów.

  • Zaimplementuj coś, co zapewnia funkcjonalność automappera specjalnie do testowania. Możesz łatwo napisać małe narzędzie, które użyje refleksji do wypełnienia twojego obiektu ze słownika zawierającego nazwy i wartości właściwości.

Spójrz również na to pytanie i odpowiedź: czy wolisz, aby prywatne rzeczy były wewnętrzne / publiczne do testów, czy użyć jakiegoś hacka, takiego jak PrivateObject?

Mike Nakis
źródło
3

Nie waham się używać refleksji do takich rzeczy w testach.

Nie lubię robić wirtualnych rzeczy na kpiny, ponieważ zmieniają kod z niewłaściwego powodu.

Nie znam automappera, ale zgadzam się z @Mike, że włączenie go do testów może być dobrym pomysłem. Test jednostki rozróżnienia / test integracji nie jest zbyt interesujący. Pewnie, jeśli zestaw testowy robi się duży i wolny, będziesz musiał filtrować i klasyfikować rzeczy, aby uruchomić tylko rozsądny podzbiór wszystkich testów z najwyższą częstotliwością.

Przykładowy hack za pomocą odbicia, użycie nameof () będzie miało lepszą perf, ale wtedy stracisz typy:

public static class TestExtensions
{
    public static void SetProperty<TSource, TProperty>(
        this TSource source,
        Expression<Func<TSource, TProperty>> prop,
        TProperty value)
    {
        var propertyInfo = (PropertyInfo)((MemberExpression)prop.Body).Member;
        propertyInfo.SetValue(source, value);
    }
}
Johan Larsson
źródło
0

Do celów testowania jednostkowego skorzystaj z frameworkowych struktur, takich jak Microsoft Fakes, TypeMock i JustMock, które zapewniają obsługę kpiącym członkom prywatnym.

Zobacz także Smocks (dostępne pakiety @nuget). Ograniczeniem Smocks jest to, że nie zapewni on dostępu prywatnym członkom. Ale ma zdolność kpienia ze statycznych i nie-wirtualnych członków. Jest również dostępny bezpłatnie.

Innym najprostszym sposobem jest użycie PrivateObject / PrivateType.

Karthik V.
źródło