Piszę parser i jako część tego mam Expander
klasę, która „rozwija” jedną złożoną instrukcję w wiele prostych instrukcji. Na przykład rozwinąłby to:
x = 2 + 3 * a
w:
tmp1 = 3 * a
x = 2 + tmp1
Teraz zastanawiam się, jak przetestować tę klasę, a konkretnie jak zorganizować testy. Mógłbym ręcznie utworzyć wejściowe drzewo składni:
var input = new AssignStatement(
new Variable("x"),
new BinaryExpression(
new Constant(2),
BinaryOperator.Plus,
new BinaryExpression(new Constant(3), BinaryOperator.Multiply, new Variable("a"))));
Lub mógłbym napisać go jako ciąg i przeanalizować:
var input = new Parser().ParseStatement("x = 2 + 3 * a");
Druga opcja jest znacznie prostsza, krótsza i czytelna. Ale wprowadza także denpendencję Parser
, co oznacza, że błąd w tym Parser
może zakończyć się niepowodzeniem. Test przestałby być testem jednostkowym Expander
i wydaje mi się, że technicznie staje się testem integracyjnym Parser
i Expander
.
Moje pytanie brzmi: czy można polegać głównie (lub całkowicie) na tego rodzaju testach integracyjnych w celu przetestowania tej Expander
klasy?
Parser
może się nie powieść w jakimś innym teście, nie stanowi problemu, jeśli zwykle popełniasz tylko błędy zerowe, przeciwnie, oznacza to, że masz większy zasięgParser
. Wolałbym się martwić, że błądParser
może sprawić, że test się powiedzie, gdy powinien się nie udać . W końcu testy jednostkowe służą do znajdowania błędów - test jest przerywany, kiedy nie, ale powinien.Odpowiedzi:
Przekonasz się, że piszesz o wiele więcej testów, o wiele bardziej skomplikowanych, interesujących i przydatnych zachowaniach, jeśli możesz to zrobić po prostu. Więc opcja, która obejmuje
jest całkiem poprawny. To zależy od innego komponentu. Ale wszystko zależy od dziesiątek innych komponentów. Jeśli wyśmiewasz coś w ciągu jednego cala jego życia, prawdopodobnie zależy Ci od wielu drwiących funkcji i urządzeń testowych.
Czasami programiści nadmiernie koncentrują się na czystości swoich testów jednostkowych lub opracowują tylko testy jednostkowe i testy jednostkowe , bez żadnego modułu, integracji, obciążenia lub innych testów. Wszystkie te formularze są ważne i użyteczne, a wszystkie ponoszą właściwą odpowiedzialność za programistów - nie tylko Q / A lub personel operacyjny w dalszej części rurociągu.
Jednym z zastosowanych przeze mnie podejść jest rozpoczęcie od testów wyższego poziomu, a następnie wykorzystanie uzyskanych z nich danych do skonstruowania długiej formy testu o najniższym wspólnym mianowniku. Np. Kiedy zrzucisz strukturę danych z
input
utworzonego powyżej, możesz łatwo zbudować:rodzaj testu, który sprawdza się na najniższym poziomie. W ten sposób otrzymujesz niezłą mieszankę: garść najbardziej podstawowych, prymitywnych testów (czyste testy jednostkowe), ale nie spędziłem tygodnia na pisaniu testów na tym prymitywnym poziomie. To daje ci czas potrzebny na napisanie o wiele więcej, nieco mniej testów atomowych przy pomocy
Parser
jako pomocnika. Wynik końcowy: więcej testów, większy zasięg, więcej narożników i inne ciekawe przypadki, lepszy kod i wyższa kontrola jakości.źródło
Oczywiście, że jest OK!
Zawsze potrzebujesz testu funkcjonalnego / integracyjnego, który wykonuje pełną ścieżkę kodu. A pełna ścieżka kodu w tym przypadku oznacza łącznie ocenę wygenerowanego kodu. Oznacza to, że parsowanie
x = 2 + 3 * a
powoduje wygenerowanie kodu, który po uruchomieniua = 5
ustawix
na,17
a jeślia = -2
zostanie uruchomionyx
na, ustawi się na-4
.Poniżej należy wykonać testy jednostkowe dla mniejszych bitów, o ile faktycznie pomaga to w debugowaniu kodu . Im drobniejsze testy będziesz miał, tym większe prawdopodobieństwo, że każda zmiana kodu będzie musiała zmienić test, ponieważ zmienia się interfejs wewnętrzny. Taki test ma niewielką wartość długoterminową i wymaga dodatkowych prac konserwacyjnych. Jest więc punkt malejących zwrotów i powinieneś przestać przed nim.
źródło
Testy jednostkowe pozwalają ustalić, które elementy się psują i gdzie złamały kod. Są więc dobre do bardzo drobnoziarnistych testów. Dobre testy jednostkowe pomogą skrócić czas debugowania.
Jednak z mojego doświadczenia wynika, że testy jednostkowe rzadko są wystarczająco dobre, aby faktycznie sprawdzić poprawność działania. Dlatego testy integracyjne są również pomocne w celu weryfikacji łańcucha lub sekwencji operacji. Testy integracyjne pomogą ci przejść przez testy funkcjonalne. Jak już wskazałeś, ze względu na złożoność testów integracyjnych trudniej jest znaleźć określone miejsce w kodzie, w którym test się psuje. Ma również nieco większą kruchość, ponieważ awarie w dowolnym miejscu w łańcuchu spowodują niepowodzenie testu. Wciąż będziesz mieć ten łańcuch w kodzie produkcyjnym, więc testowanie rzeczywistego łańcucha jest nadal pomocne.
Idealnie byłoby mieć oba, ale w każdym razie ogólnie automatyczny test jest lepszy niż brak testu.
źródło
Wykonaj wiele testów na parserze, a gdy parser przejdzie testy, zapisz te wyniki w pliku, aby wyśmiewać parser i przetestować inny komponent.
źródło