Michał Stocki
Young Digital Planet, Styczeń 2017
Warto zawsze z wyjątkiem sytuacji,
kiedy nie bardzo się da:
Typowe choroby testów pisanych po implementacji:
Powinniśmy pisać testy do każdego projektu, który wychodzi z fazy prototypu.
Rozwijanie jakiegokolwiek projektu, który nie jest pokryty testami, wiąże się z dużym ryzykiem, a ryzyko to będzie rosło wraz z wiekiem i rozmiarem projektu.
Nie każdy projekt powinien mieć jednak testy jednostkowe. Dla większości małych projektów wystarczą testy e2e (w różnej postaci).
I tak i nie. Każda logika powinna być pokryta testami, co nie oznacza, że każda klasa czy funkcja wymaga odrębnego zestawu testów
Przykłady:
Test jednostkowy powinien pokrywać najmniejszą część kodu, która może zostać użyta ponownie, lub mniejszą, jeśli jest to konieczne ze względu na dużą liczbę przypadków testowych
W przeciwnym wypadku ryzykujemy, że logika nie przestanie być testowana w momencie, kiedy usuniemy kontekst użycia, w którym została przetestowana. Przykłady:Tak, ale nie jednostkowo.
Używajmy do tego testów end-to-end
Z mojego doświadczenia wynika, że graniczy z cudem napisanie testu do dyrektywy przed jej implementacją. To z kolei oznacza, że testy będą słabe. Nawet jeśli się napisać test najpierw, będzie to zawsze test ściśle związany z implementacją – ten sam efekt wizualno-funkcjonalny możemy bowiem osiągnąć żonglując HTML-em na dziesiątki sposobów.
Środowisko do testów e2e posia odpowiednie narzędzia do potwierdzania poprawnego działania widoków. Testy jednostkowe się do tego nie nadają. Starajmy się więc całą logikę wydzielić poza dyrektywę i dopiero tam przetestować jednostkowo.
Nie. Zdecydowanie nie powinno się tego robić.
Jedną z podstawowych cech testów jednostkowych powinna być ich deterministyczność.
Test, który wywala się raz na 300 uruchomień nie jest do niczego przydatny.
Pokusą do losowania wartości użytych w teście może być:
Przydatnych może być kilka wskazówek:
describe('PageTextRangeMerger', () => {
describe('merging given list of page text ranges into a single page text range', () => {
describe('when more then one range were given', () => {
it('returns a new range which begin index equals the earliest begin index within the given ranges' +
'and end index equals the farthest end index within the given ranges', () => {
// ...
});
it('returns a new range with have page id of the first given range to be merged', () => {
// ...
});
});
describe('when the only one range was given', () => {
it('returns a new range being equal the given range', () => {
// ...
});
});
});
});
Nie! To jeden z najczęstrzych błędów
nie powinniśmy używać nazw zmiennych ani metod – nie zrefactorują się automatycznie, a poza tym skoro ktoś czyta nazwę testu, oznacza to, że nazwa metody nie była dla niego wystarczająco jasna
To zależy.
Testy parametryzowane dobrze sprawdzają się tam gdzie mamy wiele podobnych przypadków a nie mamy rozgałęzień logiki. Warto jednak w testach parametryzowanych nazwać słownie jaki przypadek brzegowy jest ilustrowany przez konkretny zestaw danych testowych.
Warto mockować wszędzie tam, gdzie użycie prawdziwej klasy jest kosztowne.
Nie. Zdecydowanie nie powinno się tego robić.
Mockując zależności narażamy się na to, że nie wykryjemy regresu spowodowanego przez update zależności. Niskopoziomowe zależności (np. lodash) traktujmy jak nasz własny kod – testujmy tak jak by wcale nie zostały użyte w implementacji. Wysokopoziomowe zależności (np. video.js) abudowujemy adapterem, w celu uniknięcia negatywnych skutków zmiany API.
Zdecdowanie nie.
Zawsze trzeba przetestować wszystkie ścieżki, jednak zbyt duża ich liczba w jednym zestawie testów jest wyraźnym sygnałem, że mamy źle wydzielone klasy
Nie. Testujemy tylko to, czego się spodziewamy.
Niekoniecznie. Czasem do potwierdzenia jednego zachowania, potrzebnych jest kilka asercji.