Uważam, że jest to nieporozumienie w każdy możliwy sposób.
Kod testowy testujący kod produkcyjny wcale nie jest podobny. Pokażę w Pythonie:
def multiply(a, b):
"""Multiply ``a`` by ``b``"""
return a*b
Wtedy prosty test byłby:
def test_multiply():
assert multiply(4, 5) == 20
Obie funkcje mają podobną definicję, ale obie robią bardzo różne rzeczy. Tutaj nie ma duplikatu kodu. ;-)
Zdarza się również, że ludzie piszą zduplikowane testy, zasadniczo mając jedno stwierdzenie na funkcję testową. To jest szaleństwo i widziałem, jak ludzie to robią. To jest złą praktyką.
def test_multiply_1_and_3():
"""Assert that a multiplication of 1 and 3 is 3."""
assert multiply(1, 3) == 3
def test_multiply_1_and_7():
"""Assert that a multiplication of 1 and 7 is 7."""
assert multiply(1, 7) == 7
def test_multiply_3_and_4():
"""Assert that a multiplication of 3 and 4 is 12."""
assert multiply(3, 4) == 12
Wyobraź sobie, że robisz to dla ponad 1000 efektywnych wierszy kodu. Zamiast tego testujesz według „funkcji”:
def test_multiply_positive():
"""Assert that positive numbers can be multiplied."""
assert multiply(1, 3) == 3
assert multiply(1, 7) == 7
assert multiply(3, 4) == 12
def test_multiply_negative():
"""Assert that negative numbers can be multiplied."""
assert multiply(1, -3) == -3
assert multiply(-1, -7) == 7
assert multiply(-3, 4) == -12
Teraz, gdy funkcje są dodawane / usuwane, muszę jedynie rozważyć dodanie / usunięcie jednej funkcji testowej.
Być może zauważyłeś, że nie zastosowałem for
pętli. Jest tak, ponieważ powtarzanie niektórych rzeczy jest dobre. Gdybym zastosował pętle, kod byłby znacznie krótszy. Ale gdy asercja nie powiedzie się, może zaciemnić wyjście wyświetlając niejednoznaczny komunikat. Jeśli to nastąpi wtedy twoje testy będą mniej przydatne i będzie potrzebować debuggera aby sprawdzić, gdzie rzeczy nie udać.
assert multiply(1,3)
nie powiedzie się, ale nie otrzymasz również raportu z nieudanego testuassert multiply(3,4)
.def test_shuffle
wykonuje dwa twierdzenia.assert multiply(*, *) == *
abyś mógł zdefiniowaćassert_multiply
funkcję. W bieżącym scenariuszu nie ma znaczenia liczba wierszy i czytelność, ale przy dłuższych testach można ponownie użyć skomplikowanych asercji, urządzeń, kodu generującego urządzenia itp. Nie wiem, czy jest to najlepsza praktyka, ale zazwyczaj robię to to.Nie, to nie jest prawda.
Testy mają inny cel niż implementacja:
źródło
Nie. DRY polega na pisaniu kodu tylko raz, aby wykonać określone zadanie. Testy sprawdzają poprawność wykonania zadania. Jest to trochę podobne do algorytmu głosowania, w którym oczywiście użycie tego samego kodu byłoby bezużyteczne.
źródło
Nie, ostateczny cel DRY oznaczałoby wyeliminowanie całego kodu produkcyjnego .
Jeśli nasze testy mogłyby być idealnymi specyfikacjami tego, co system ma zrobić, musielibyśmy automatycznie wygenerować odpowiedni kod produkcyjny (lub pliki binarne), skutecznie usuwając bazę kodu produkcyjnego per se.
To jest właśnie to, co podobno osiąga architektura oparta na modelach - jedno zaprojektowane przez człowieka źródło prawdy, z którego wszystko wynika z obliczeń.
Nie sądzę, aby sytuacja odwrotna (pozbycie się wszystkich testów) była pożądana, ponieważ:
źródło
Ponieważ czasami powtarzanie się jest w porządku. Żadnej z tych zasad nie należy traktować w każdych okolicznościach bez pytania lub kontekstu. Czasami pisałem testy przeciwko naiwnej (i powolnej) wersji algorytmu, co jest dość wyraźnym naruszeniem DRY, ale zdecydowanie korzystne.
źródło
Ponieważ testy jednostkowe mają na celu utrudnienie niezamierzonych zmian , czasami może to utrudnić również zamierzone zmiany . Fakt ten jest rzeczywiście związany z zasadą SUCHEGO.
Na przykład, jeśli masz funkcję,
MyFunction
która jest wywoływana w kodzie produkcyjnym tylko w jednym miejscu i piszesz dla niej 20 testów jednostkowych, możesz łatwo mieć 21 miejsc w kodzie, w których ta funkcja jest wywoływana. Teraz, gdy musisz zmienić podpisMyFunction
, semantykę lub oba (ponieważ zmieniają się niektóre wymagania), masz 21 miejsc do zmiany zamiast tylko jednego. Powodem jest naruszenie zasady OSUSZANIA: powtórzyłeś (przynajmniej) to samo wywołanie funkcji doMyFunction
21 razy.Prawidłowym podejściem w takim przypadku jest również zastosowanie zasady OSUSZANIA do kodu testowego: podczas pisania 20 testów jednostkowych obuduj wywołania
MyFunction
w testach jednostkowych tylko kilkoma funkcjami pomocniczymi (najlepiej tylko jedną), które są używane przez 20 testów jednostkowych. Idealnie byłoby, gdybyś miał tylko dwa miejsca w wywołaniu koduMyFunction
: jedno z kodu produkcyjnego i jedno z testów jednostkowych. Więc kiedy będziesz musiał zmienić podpisMyFunction
później, będziesz mieć tylko kilka miejsc do zmiany w swoich testach.„Kilka miejsc” to wciąż więcej niż „jedno miejsce” (to, co otrzymujesz bez testów jednostkowych), ale zalety posiadania testów jednostkowych powinny znacznie przewyższać korzyści wynikające z mniejszej ilości kodu do zmiany (w przeciwnym razie przeprowadzasz pełne testy jednostkowe źle).
źródło
Jednym z największych wyzwań związanych z budowaniem oprogramowania jest wychwycenie wymagań; to jest odpowiedź na pytanie „co powinno zrobić to oprogramowanie?” Oprogramowanie wymaga dokładnych wymagań, aby dokładnie określić, co powinien zrobić system, ale ci, którzy określają potrzeby systemów oprogramowania i projektów, często obejmują osoby, które nie mają doświadczenia programowego lub formalnego (matematyki). Brak rygorystyczności w definiowaniu wymagań zmusił programistów do znalezienia sposobu na sprawdzenie oprogramowania pod kątem wymagań.
Zespół programistów przełożył potoczny opis projektu na bardziej rygorystyczne wymagania. Dyscyplina testowania połączyła się jako punkt kontrolny dla rozwoju oprogramowania, aby wypełnić lukę między tym, co klient mówi, że chce, a tym, co oprogramowanie rozumie, czego chce. Zarówno twórcy oprogramowania, jak i zespół ds. Jakości / testowania rozumieją (nieformalną) specyfikację i każdy (niezależnie) pisze oprogramowanie lub testy, aby upewnić się, że ich zrozumienie jest zgodne. Dodanie innej osoby w celu zrozumienia (nieprecyzyjnych) wymagań dodało pytania i inną perspektywę w celu dalszego doskonalenia precyzji wymagań.
Ponieważ zawsze istniały testy akceptacyjne, naturalnym było rozszerzenie roli testowania o pisanie testów automatycznych i jednostkowych. Problem polegał na zatrudnianiu programistów do testowania, a tym samym zawęziłeś perspektywę od zapewniania jakości do programistów wykonujących testy.
To powiedziawszy, prawdopodobnie źle wykonujesz testy, jeśli twoje testy niewiele różnią się od rzeczywistych programów. Sugestią Msdy byłoby skupienie się bardziej na tym, co w testach, a mniej na tym, jak.
Ironią jest to, że zamiast przechwycić formalną specyfikację wymagań z potocznego opisu, przemysł postanowił zaimplementować testy punktowe jako kod do automatyzacji testów. Zamiast tworzyć formalne wymagania, które oprogramowanie mogłoby zbudować, aby odpowiedzieć, przyjęto podejście polegające na przetestowaniu kilku punktów, zamiast podejściu do tworzenia oprogramowania przy użyciu formalnej logiki. Jest to kompromis, ale był dość skuteczny i stosunkowo udany.
źródło
Jeśli uważasz, że kod testowy jest zbyt podobny do kodu implementacyjnego, może to wskazywać, że nadmiernie używasz fałszywego frameworka. Testy próbne na zbyt niskim poziomie mogą zakończyć się konfiguracją testową przypominającą testowaną metodę. Spróbuj napisać testy wyższego poziomu, które rzadziej ulegną awarii, jeśli zmienisz implementację (wiem, że może to być trudne, ale jeśli możesz to zarządzać, otrzymasz bardziej przydatny zestaw testów).
źródło
Testy jednostkowe nie powinny obejmować powielania testowanego kodu, jak już wspomniano.
Dodałbym jednak, że testy jednostkowe zwykle nie są tak SUCHE jak kod „produkcyjny”, ponieważ konfiguracja zwykle jest podobna (ale nie identyczna) między testami ... zwłaszcza jeśli masz znaczną liczbę zależności, które kpisz / udawanie.
Oczywiście jest możliwe przeformułowanie tego rodzaju rzeczy na wspólną metodę konfiguracji (lub zestaw metod konfiguracji) ... ale odkryłem, że te metody konfiguracji mają zwykle długie listy parametrów i są raczej kruche.
Bądź więc pragmatyczny. Jeśli możesz skonsolidować kod instalacyjny bez uszczerbku dla łatwości konserwacji, zrób to. Ale jeśli alternatywą jest złożony i kruchy zestaw metod konfiguracji, trochę powtórzeń w metodach testowych jest OK.
Lokalny ewangelista TDD / BDD ujmuje to następująco:
„Twój kod produkcyjny powinien być SUCHY. Ale twoje testy mogą być„ wilgotne ”.
źródło
To nie jest prawda, testy opisują przypadki użycia, podczas gdy kod opisuje algorytm, który przekazuje przypadki użycia, co jest bardziej ogólne. W TDD zaczynasz od pisania przypadków użycia (prawdopodobnie na podstawie historii użytkownika), a następnie implementujesz kod niezbędny do przekazania tych przypadków użycia. Więc piszesz mały test, małą część kodu, a następnie refaktoryzujesz, jeśli to konieczne, aby pozbyć się powtórzeń. Tak to działa.
Testami mogą być również powtórzenia. Na przykład możesz ponownie użyć urządzeń, kodu generującego urządzenia, skomplikowanych asercji itp. Zazwyczaj robię to, aby uniknąć błędów w testach, ale zwykle zapominam najpierw przetestować, czy test naprawdę się nie powiedzie i może naprawdę zrujnować dzień , kiedy przez pół godziny szukasz błędu w kodzie, a test jest nieprawidłowy ... xD
źródło