Mam więc moduł uwierzytelniania, który napisałem jakiś czas temu. Teraz widzę błędy na mojej drodze i piszę dla nich testy jednostkowe. Podczas pisania testów jednostkowych trudno mi wymyślić dobre nazwiska i dobre obszary do przetestowania. Na przykład mam takie rzeczy
- Wymaga Logowania_ powinien być_redirect_when_not_logged_in
- Wymaga Logowania_powinna_przejść_przejście_w momencie_logowania_w
- Login_should_work_when_given_proper_credentials
Osobiście uważam, że jest to trochę brzydkie, chociaż wydaje się „właściwe”. Mam również problemy z rozróżnieniem testów, po prostu skanując je (muszę przynajmniej dwukrotnie przeczytać nazwę metody, aby wiedzieć, co się nie udało)
Pomyślałem więc, że może zamiast pisać testy, które czysto testują funkcjonalność, może napisz zestaw testów obejmujących scenariusze.
Na przykład jest to testowy odcinek, który wymyśliłem:
public class Authentication_Bill
{
public void Bill_has_no_account()
{ //assert username "bill" not in UserStore
}
public void Bill_attempts_to_post_comment_but_is_redirected_to_login()
{ //Calls RequiredLogin and should redirect to login page
}
public void Bill_creates_account()
{ //pretend the login page doubled as registration and he made an account. Add the account here
}
public void Bill_logs_in_with_new_account()
{ //Login("bill", "password"). Assert not redirected to login page
}
public void Bill_can_now_post_comment()
{ //Calls RequiredLogin, but should not kill request or redirect to login page
}
}
Czy to słyszy się wzór? Widziałem historie akceptacji i takie tam, ale jest to zupełnie inna sytuacja. Duża różnica polega na tym, że wymyślam scenariusze „wymuszenia” testów. Zamiast ręcznie próbować wymyślić możliwe interakcje, które będę musiał przetestować. Wiem też, że zachęca to do testów jednostkowych, które nie testują dokładnie jednej metody i klasy. Myślę, że to jest OK. Wiem też, że spowoduje to problemy przynajmniej dla niektórych środowisk testowych, ponieważ zwykle zakładają, że testy są od siebie niezależne i kolejność nie ma znaczenia (gdzie to by było w tym przypadku).
W każdym razie, czy jest to w ogóle wskazany wzór? Czy byłoby to idealne dopasowanie do testów integracyjnych mojego interfejsu API, a nie jako testów „jednostkowych”? To tylko osobisty projekt, więc jestem otwarty na eksperymenty, które mogą się nie udać.
_test
dołączonym i używam komentarzy, aby zanotować oczekiwane wyniki. Jeśli jest to projekt osobisty, znajdź styl, w którym czujesz się komfortowo i trzymaj się tego.Odpowiedzi:
Tak, dobrym pomysłem jest podanie nazw testów przykładowych scenariuszy, które testujesz. Używanie narzędzia do testowania jednostek nie tylko do testów jednostkowych może być w porządku, wiele osób robi to z sukcesem (ja też).
Ale nie, zdecydowanie nie jest dobrym pomysłem pisanie testów w sposób, w którym liczy się kolejność wykonywania testów. Na przykład NUnit pozwala użytkownikowi wybrać interaktywnie, który test chce on / ona zostać wykonany, więc to nie będzie działać w zamierzony sposób.
Można tego łatwo uniknąć, oddzielając główną część testową każdego testu (w tym „aser”) od części, które ustawiają system w prawidłowym stanie początkowym. Korzystając z powyższego przykładu: napisz metody zakładania konta, logowania i zamieszczania komentarzy - bez żadnego potwierdzenia. Następnie ponownie zastosuj te metody w różnych testach. Będziesz także musiał dodać kod do
[Setup]
metody urządzeń testowych, aby upewnić się, że system jest w prawidłowo zdefiniowanym stanie początkowym (na przykład brak kont w bazie danych, nikt do tej pory nie był podłączony itp.).EDYCJA: Oczywiście wydaje się, że jest to sprzeczne z „historią” natury twoich testów, ale jeśli nadasz metodom pomocniczym sensowne nazwy, znajdziesz swoje historie w ramach każdego testu.
Powinno to wyglądać tak:
źródło
Problem z opowiadaniem historii za pomocą testów jednostkowych polega na tym, że nie jest jasne, że testy jednostkowe powinny być ustawione i przeprowadzane całkowicie niezależnie od siebie.
Dobry test jednostkowy powinien być całkowicie odizolowany od wszystkich innych kodów zależnych, jest to najmniejsza jednostka kodu, którą można przetestować.
Daje to tę korzyść, że oprócz potwierdzenia działania kodu, jeśli test się nie powiedzie, otrzymasz bezpłatną diagnozę dokładnie tego, gdzie kod jest nieprawidłowy. Jeśli test nie jest izolowany, musisz sprawdzić, od czego zależy, aby dowiedzieć się dokładnie, co poszło nie tak, i przegapić główną zaletę testów jednostkowych. Posiadanie kolejności wykonania może również wywoływać wiele fałszywych negatywów, jeśli test się nie powiedzie, kolejne testy mogą się nie powieść pomimo kodu, który test działa idealnie.
Dobry artykuł bardziej szczegółowo to klasyk na temat brudnych testów hybrydowych .
Aby klasy, metody i wyniki były czytelne, wielki test Art of Unit wykorzystuje konwencję nazewnictwa
Klasa testowa:
Metody testowe:
Aby skopiować przykład @Doc Brown, zamiast korzystać z [Setup], który uruchamia się przed każdym testem, piszę metody pomocnicze do budowania izolowanych obiektów do testowania.
Testy zakończone niepowodzeniem mają sensowną nazwę, która daje pewną narrację o tym, która metoda zawiodła, warunek i oczekiwany wynik.
Tak zawsze pisałem testy jednostkowe, ale przyjaciel odniósł duży sukces z Gerkinem .
źródło
To, co opisujesz, bardziej przypomina mi Behaviour Driven Design (BDD) niż testowanie jednostkowe. Spójrz na SpecFlow, która jest technologią .NET BDD opartą na DSL Gherkin .
Potężne rzeczy, które każdy człowiek może czytać / pisać bez wiedzy o kodowaniu. Nasz zespół testowy odnosi wielki sukces, wykorzystując go w naszych pakietach testów integracji.
Jeśli chodzi o konwencje dotyczące testów jednostkowych, odpowiedź @ DocBrown wydaje się solidna.
źródło
assert(value === expected)
BDD =value.should.equals(expected)
+ opisujesz cechy warstw, które rozwiązują problem „niezależności testu jednostkowego”. To świetny styl!