Testowanie ze wszystkimi istniejącymi zależnościami jest nadal ważne, ale bardziej dotyczy sfery testów integracyjnych, jak powiedział Jeffrey Faust.
Jednym z najważniejszych aspektów testów jednostkowych jest zapewnienie wiarygodności testów. Jeśli nie ufasz, że pozytywny test naprawdę oznacza, że wszystko jest w porządku, a test zakończony niepowodzeniem naprawdę oznacza problem w kodzie produkcyjnym, twoje testy nie są tak przydatne, jak mogą być.
Aby twoje testy były wiarygodne, musisz zrobić kilka rzeczy, ale skupię się tylko na jednej z tych odpowiedzi. Musisz upewnić się, że są łatwe do uruchomienia, aby wszyscy programiści mogli je łatwo uruchomić przed wpisaniem kodu. „Łatwy do uruchomienia” oznacza, że twoje testy działają szybko i nie jest wymagana żadna rozbudowana konfiguracja ani konfiguracja, aby je uruchomić. Idealnie byłoby, gdyby każdy mógł sprawdzić najnowszą wersję kodu, uruchomić testy od razu i zobaczyć, jak się sprawdzają.
Pozbycie się zależności od innych rzeczy (system plików, baza danych, usługi sieciowe itp.) Pozwala uniknąć konieczności konfigurowania i sprawia, że ty i inni programiści mniej podatni na sytuacje, w których kusi cię, by powiedzieć: „Och, testy nie powiodły się, ponieważ ja nie” mieć skonfigurowany udział sieciowy. No cóż. Uruchomię je później. ”
Jeśli chcesz przetestować to, co robisz z niektórymi danymi, testy jednostkowe tego kodu logiki biznesowej nie powinny obchodzić, w jaki sposób otrzymujesz te dane. Możliwość przetestowania podstawowej logiki aplikacji bez polegania na wsparciu takich rzeczy jak bazy danych jest niesamowita. Jeśli tego nie robisz, tracisz.
PS Powinienem dodać, że zdecydowanie można nadpisać inżynierię w imię testowalności. Testowanie aplikacji pomaga złagodzić ten problem. Ale w każdym razie słabe implementacje podejścia nie powodują, że podejście jest mniej ważne. Wszystko może być niewłaściwie wykorzystane i przesadzone, jeśli nie będzie się pytać „dlaczego to robię?” podczas rozwoju.
Jeśli chodzi o wewnętrzne zależności, sprawy stają się trochę mętne. Lubię o tym myśleć, że chcę chronić moją klasę w jak największym stopniu przed zmianą z niewłaściwych powodów. Jeśli mam konfigurację taką jak ta ...
public class MyClass
{
private SomeClass someClass;
public MyClass()
{
someClass = new SomeClass();
}
// use someClass in some way
}
Generalnie nie dbam o SomeClass
to, jak powstaje. Chcę tylko tego użyć. Jeśli SomeClass zmieni się i teraz wymaga parametrów do konstruktora ... to nie jest mój problem. Nie powinienem zmieniać MyClass, aby to uwzględnić.
Teraz dotyczy to tylko części projektowej. Jeśli chodzi o testy jednostkowe, chcę również chronić się przed innymi klasami. Jeśli testuję MyClass, lubię wiedzieć, że nie ma żadnych zewnętrznych zależności, że SomeClass w pewnym momencie nie wprowadził połączenia z bazą danych lub innego zewnętrznego łącza.
Ale jeszcze większym problemem jest to, że wiem również, że niektóre wyniki moich metod opierają się na danych wyjściowych z niektórych metod na SomeClass. Bez wyśmiewania / eliminowania SomeClass, mógłbym nie mieć możliwości różnicowania zapotrzebowania na dane wejściowe. Jeśli mi się poszczęści, być może uda mi się skomponować moje środowisko w teście w taki sposób, że wyzwoli właściwą odpowiedź z SomeClass, ale pójście w ten sposób wprowadza złożoność do moich testów i czyni je kruchymi.
Przepisanie MyClass, aby zaakceptowało wystąpienie SomeClass w konstruktorze, pozwala mi utworzyć fałszywe wystąpienie SomeClass, które zwraca żądaną wartość (za pośrednictwem frameworku lub ręcznie). W tym przypadku generalnie nie muszę wprowadzać interfejsu. To, czy to zrobić, czy nie, jest pod wieloma względami osobistym wyborem, który może być podyktowany twoim wybranym językiem (np. Interfejsy są bardziej prawdopodobne w C #, ale na pewno nie potrzebujesz takiego w Ruby).