Mam test jednostkowy, który wygląda następująco:
[Test]
public void Should_create_person()
{
Assert.DoesNotThrow(() => new Person(Guid.NewGuid(), new DateTime(1972, 01, 01));
}
Zapewniam, że tutaj utworzono obiekt Person, tzn. Że sprawdzanie poprawności nie kończy się niepowodzeniem. Na przykład, jeśli Guid ma wartość zerową lub data urodzenia jest wcześniejsza niż 01.01.1900, wówczas sprawdzanie poprawności zakończy się niepowodzeniem i zostanie zgłoszony wyjątek (co oznacza, że test się nie powiedzie).
Konstruktor wygląda następująco:
public Person(Id id, DateTime dateOfBirth) :
base(id)
{
if (dateOfBirth == null)
throw new ArgumentNullException("Date of Birth");
elseif (dateOfBith < new DateTime(1900,01,01)
throw new ArgumentException("Date of Birth");
DateOfBirth = dateOfBirth;
}
Czy to dobry pomysł na test?
Uwaga : kieruję się klasycystycznym podejściem do testów jednostkowych modelu domeny, jeśli ma to jakiś wpływ.
c#
unit-testing
constructors
w0051977
źródło
źródło
Should_create_person
? Co powinno stworzyć osobę? Nadaj mu sensowną nazwę, na przykładCreating_person_with_valid_data_succeeds
.Odpowiedzi:
Jest to poprawny test (choć raczej nadgorliwy) i czasami robię to, aby przetestować logikę konstruktora, jednak jak wspomniał Laiv w komentarzach, powinieneś zadać sobie pytanie, dlaczego.
Jeśli twój konstruktor wygląda tak:
Czy warto sprawdzać, czy rzuca? Czy parametry są poprawnie przypisane, rozumiem, ale twój test jest raczej przesadny.
Jeśli jednak twój test wykonuje coś takiego:
Następnie twój test staje się bardziej odpowiedni (ponieważ w rzeczywistości rzucasz wyjątki gdzieś w kodzie).
Powiem jedno, ogólnie rzecz biorąc, złą praktyką jest posiadanie dużej logiki w konstruktorze. Podstawowe sprawdzanie poprawności (takie jak kontrole zerowe / domyślne, które wykonuję powyżej) jest w porządku. Ale jeśli łączysz się z bazami danych i ładujesz czyjeś dane, wtedy kod zaczyna naprawdę wąchać ...
Z tego powodu, jeśli twój konstruktor jest wart przetestowania (ponieważ jest dużo logiki), to może coś innego jest nie tak.
Prawie na pewno będziesz mieć inne testy obejmujące tę klasę w warstwach logiki biznesowej, konstruktory i przypisania zmiennych prawie na pewno uzyskają pełne pokrycie z tych testów. Dlatego może nie ma sensu dodawanie konkretnych testów specjalnie dla konstruktora. Jednak nic nie jest czarno-białe i nie miałbym nic przeciwko tym testom, gdybym je sprawdzał - ale zastanawiałbym się, czy dodają one dużej wartości ponad testy poza gdzie indziej w twoim rozwiązaniu.
W twoim przykładzie:
Nie tylko sprawdzasz poprawność, ale również wywołujesz konstruktora podstawowego. Dla mnie daje to więcej powodów do przeprowadzania tych testów, ponieważ logika konstruktora / sprawdzania poprawności jest teraz podzielona na dwie klasy, co zmniejsza widoczność i zwiększa ryzyko nieoczekiwanej zmiany.
TLDR
Testy te mają pewną wartość, jednak logika walidacji / przypisania prawdopodobnie będzie objęta innymi testami w twoim rozwiązaniu. Jeśli w tych konstruktorach jest dużo logiki, która wymaga znacznych testów, to sugeruje mi, że czai się tam nieprzyjemny zapach kodu.
źródło
PersonBirthdate
), Która dokonuje daty walidacji urodzenia. PodobnieGuid
sprawdzenie może zostać zaimplementowane wId
klasie. Oznacza to, że tak naprawdę nie musisz już mieć tej logiki sprawdzania poprawności wPerson
konstruktorze, ponieważ nie jest możliwe zbudowanie takiej z niepoprawnymi danymi - z wyjątkiemnull
referencji. Oczywiście musisz napisać testy dla pozostałych dwóch klas :)Już dobra odpowiedź tutaj, ale myślę, że warto wspomnieć o jeszcze jednej rzeczy.
Robiąc TDD „według książki”, należy najpierw napisać test, który wywołuje konstruktor, nawet zanim ten konstrukt zostanie zaimplementowany. Test ten może wyglądać tak jak ten, który przedstawiłeś, nawet jeśli w implementacji konstruktora nie byłoby logiki zerowej weryfikacji.
Zauważ też, że dla TDD najpierw należy napisać inny test
przed dodaniem czeku
DateTime(1900,01,01)
do konstruktora.W kontekście TDD pokazany test ma całkowicie sens.
źródło