Skopiuj i wklej kod testowy: jak źle to jest?


12

Moje obecne zadanie polega głównie na pisaniu kodu testowego GUI dla różnych aplikacji, nad którymi pracujemy. Uważam jednak, że mam tendencję do kopiowania i wklejania dużej ilości kodu w ramach testów. Powodem tego jest to, że obszary, które testuję, są na tyle podobne, że wymagają powtórzenia, ale nie dość podobne, by zamknąć kod w metodach lub obiektach. Uważam, że kiedy próbuję bardziej intensywnie korzystać z klas lub metod, testy stają się trudniejsze w utrzymaniu, a czasem wręcz trudne do napisania.

Zamiast tego zwykle kopiuję dużą część kodu testowego z jednej sekcji i wklejam go do innej i wprowadzam wszelkie drobne zmiany, których potrzebuję. Nie używam bardziej uporządkowanych sposobów kodowania, takich jak używanie większej liczby zasad OO lub funkcji.

Czy inni koderzy tak się czują podczas pisania kodu testowego? Oczywiście chcę przestrzegać zasad DRY i YAGNI, ale uważam, że kod testowy (i tak automatyczny kod testowy do testowania GUI) może utrudniać przestrzeganie tych zasad. Czy też potrzebuję po prostu więcej praktyki kodowania i lepszego ogólnego systemu robienia rzeczy?

EDYCJA: Narzędzie, którego używam, to SilkTest, który jest w zastrzeżonym języku o nazwie 4Test. Testy te dotyczą głównie aplikacji komputerowych Windows, ale testowałem również aplikacje internetowe przy użyciu tej konfiguracji.


Z jakiego narzędzia testowego korzystasz? Być może twoja struktura testowania nie obsługuje typów testów, które piszesz. Wycinanie i wklejanie więcej niż 3 wierszy jest na ogół naprawdę złe, ale jeśli jesteś w stanie wyraźnie dodać więcej długoterminowych wartości poprzez automatyzację testu GUI niż wykonywanie go ręcznie za każdym razem, to wszystko, co robisz, jest prawdopodobnie bardzo cholerne dobry.
GlenPeterson

Jaki to jest język? Państwo może mieć coś dostępne, które nie tylko pojawiały się w umyśle, który pozwoliłby ponownego wykorzystania (jak funkcje pierwszą klasą). Z drugiej strony, przypadki testowe miało być proste, aby ją zachować rzadziej mają same błędy ...
Izkata

3
We wszystkim, co napisałem, testowanie kodu nie jest wykluczone z refaktoryzacji ..
Simon Whitehead,

Odpowiedzi:


23

Skopiowane, a następnie edytowane przypadki testowe są często w porządku.

Testy powinny mieć jak najmniej zależności zewnętrznych i być możliwie jak najprostsze. Przypadki testowe zmieniają się z czasem, a wcześniej prawie identyczne przypadki testowe mogą nagle się różnić. Aktualizowanie jednego przypadku testowego bez martwienia się o uszkodzenie innych przypadków to dobra rzecz.

Oczywiście kod bojlera, który jest identyczny w wielu przypadkach testowych i musi ulec zmianie, może i powinien zostać uwzględniony.


1
Tak głównie się czuję. Prawie identyczny kod testowy jest dobry w wielu przypadkach, ale powtórzenie identycznego kodu testowego jest złą wiadomością.
joshin4colours,

12

Powtarzanie jest źródłem wszelkiego zła

Zgadza się! Powtarzanie jest źródłem wszelkiego zła . Prawdopodobnie to Knuth powiedział w swojej książce „Przedwczesna optymalizacja jest źródłem wszelkiego zła”, ale myślę, że to powtórzenie.

Ilekroć patrzysz na program lub piszesz program i odkrywasz jakieś powtórzenie: Usuń go! Zabij go natychmiast… cokolwiek, ale się go pozbądź !

Za każdym razem, gdy wprowadzałem jakieś powtórzenie i musiałem tam naprawić błąd, zapomniałem naprawić replikę ... (Donald Knuth) Tak więc, gdy jest powtórzenie, po prostu usuń je najlepiej jak potrafisz, nie rąbaj !

Pomyśl o czystym, szczupłym projekcie (np. Enkapsulowaniu powtarzających się bloków kodu w klasach pomocniczych) i napisz kilka testów, zanim coś zmienisz (aby mieć pewność, że czegoś nie złamałeś). Dotyczy to każdego napisanego kodu, a kody testowe nie są wyjątkiem.

Oto dobra lektura Code Horror, która mnie inspiruje - Skromna propozycja szkoły kopiowania i wklejania ponownego użycia kodu .


„Za każdym razem, gdy wprowadzałem jakieś powtórzenie i musiałem tam naprawić błąd, zapomniałem naprawić replikę…” dokładnie. Ponadto, jeśli c & p zapomnisz dostosować skopiowany tekst do bieżącego kontekstu, będzie to bardzo boleć. Błędny kod testowy nie brzmi jak optymalna sytuacja, prawda?
marktani

tak, wziąłem stacje z Knuth :)
Yusubov

9
Powtórzyłeś się: Powtórzyłeś się, mówiąc „Powtórzenie jest źródłem wszelkiego zła” w tytule i zdaniu wstępnym.
Thomas Eding,

Tak, zrobiłem to celowo, aby podkreślić znaczenie i zapraszamy do edytowania tego fragmentu :)
Yusubov

1
Thomas Eding, również się powtórzyłeś. Ty też się powtórzyłeś =)
marktani,

7

Nadal jest dość źle wycinać i wklejać. Jest kilka problemów.

Twoje testy mogą być kruche, ponieważ jesteś podatny na coś, co wymaga zmiany w całym tym skopiowanym i wklejonym kodzie. Czy będziesz musiał przepisać wszystkie testy?

Jeśli nie możesz enkapsulować logiki do metod pomocniczych poza testami, nie możesz pisać testów tych metod pomocniczych. Pisanie testów metod testowych jest zwykle zbyt opłacalne, ponieważ musisz złamać kod, aby przetestować test. Ale może jednostkowe metody badań pomocniczych.

Może to sprawić, że testy będą mniej czytelne. Duży blok skopiowanego kodu może być trudniejszy do odczytania niż metoda call to helper o opisowej nazwie.

Wszystko, co wymieniłem, może być problemem. Jeśli okaże się, żaden z nich faktycznie jest problem to oczywiście jest w porządku.


> wywołanie metody pomocniczej o nazwie opisowej. Nie stanowi problemu z tym, że testy jednostkowe stają się teraz programami same w sobie - których należy unikać. Co jeśli niektóre testy kończą się niepowodzeniem - czy to jest uszkodzony kod, czy pomocnicy testu?
dwjohnston

4

Kiedyś się z tobą zgadzałem. Ale potem z czasem odkryłem, że każda zmiana, którą wprowadziłem (szczególnie zmiany DI w testach jednostkowych) wymagała wielu testów do zmiany i było to uciążliwe. Teraz zapisuję się do szkoły DRY, nawet podczas pisania testów.

W przypadku testowania GUI warto przyjrzeć się wzorowi PageObject, aby zmniejszyć powtarzający się kod.


2

Polecam wybranie wzorców XUnit. Miałem dokładnie ten sam problem, dopóki nie zacząłem wykorzystywać tej książki. Do obiektu Matka dźwięki Wzorzec jak to będzie najbardziej pomocne dla scenariusza.

Jak ktoś wspomniał, prawidłowe kapsułkowanie tego kodu instalacyjnego może być uciążliwe, ale konieczność zmiany go we wszystkich miejscach, które kopiujesz i wklejasz, jest jeszcze bardziej znacząca.


+1 dla Object Mother patternwspólnego kodu inicjalizacji.
k3b

2

Czy ludzie powinni starać się ograniczać represje, kiedy tylko mogą - tak. Ale wypłata zależy od sytuacji. Mogłoby to wrócić do debaty na temat najlepszych praktyk. Ale pytanie brzmi: co jest dla ciebie najlepsze w tej sytuacji. Istnieją wyjątki od każdej reguły.

Chciałbym zapytać o kilka rzeczy: 1) Jak duże jest prawdopodobieństwo, że ta funkcjonalność testowana w UAT się zmieni? Jeśli jest mało prawdopodobne, że ulegnie zmianie, istnieje mniejsze prawdopodobieństwo, że będziesz musiał zaktualizować każdy zestaw kodów. 2) Jeśli nastąpi zmiana w UAT, czy zawsze wpłynie na każdy zestaw skopiowanego kodu, czy może wpłynie tylko na jeden lub dwa zestawy? Jeśli może być izolowany i wymaga zmiany tylko jednego zestawu, pomocne może być rozdzielenie rzeczy. 3) Jak skomplikowana będzie początkowa metoda, jeśli spróbujesz obsłużyć wszystkie scenariusze? Czy dodajesz dużo zagnieżdżonych, jeśli / else / loop? Jeśli zaczniesz przesadzać z rozgałęzianiem, możesz skończyć z trudnym do zrozumienia kodem. Czy łatwiej byłoby dokonać aktualizacji w każdym z skopiowanych tekstów, niż wrócić do całej logiki rozgałęziania?

Jeśli utkniesz, kopiuj / wklej / zmień, sądzę, że chcesz dodać komentarze, takie jak „To jest kopiowane w metodzie xyz”. W ten sposób przypomnisz sobie o konieczności aktualizacji wszystkich wklejonych wersji kodu. Lub (pochodzący od innego użytkownika SilkTest) możesz dodać osobny plik inc, który koncentruje się tylko na tym powtarzanym kodzie. W ten sposób masz wszystkie odmiany w jednym miejscu i możesz łatwo zobaczyć różne metody wymagające aktualizacji.


0

Jedna wielka procedura

Jedna myśl: brzmi to tak, jakbyś starał się unikać kodu cut-n-paste, tworząc metody takie jak:

testScreen(title, fieldList, linkList, param1, param2, param3,...) {
    test that the layout at the top of the screen is correct
    test if PageTitle == title?
    for each field in fieldList:
        check that it appears in order on the screen
    for each field in linkList:
        check that it appears in order on the screen
    test if param1 is whatever...
    test if param2 is whatever...
    etc.
    test that the bottom of the screen is correct
}

Wiele małych procedur (zestaw narzędzi)

Czy rozważałeś także odwrotne podejście? Zamiast przekazywać milion parametrów do jednej dużej procedury testScreen (), może stwórz własną platformę lub zestaw narzędzi z procedurami pomocniczymi, które wybierasz, gdy ich potrzebujesz. Lubić:

testScreenTop()
verifyLinks(list)
testScreenBottom()

Nadal wycinasz i wklejasz te procedury na każdym ekranie, ale wycinasz i wklejasz mniejsze fragmenty kodu oraz tworzysz fragmenty o wspólności, które nie są wycinane i wklejane (zawartość każdej małej procedury).

Wytnij i wklej

Jedyny czas, w którym kod mnie nie ugryzł, to to, że kod został wyrzucony, zanim musiałem go zmienić. Moje największe obawy związane z testami interfejsu użytkownika polegają na tym, jak szybko stają się one przestarzałe. Jeśli okaże się, że wyrzucasz cały kod przed jego zmianą, być może znalazłeś niszę, w której wycinanie i wklejanie jest w porządku! Ponadto, nie jest tak źle, gdy nie ma kodu poniżej wyciętego i wklejonego kodu (np. W interfejsie użytkownika aplikacji). Jeśli wklejasz więcej niż 3 linie, naprawdę chciałbym coś z tym zrobić. Przynajmniej podejmij kroki, aby go zminimalizować!

Zautomatyzowane testowanie interfejsu użytkownika

Heck, jeśli możesz udowodnić wyższą wydajność dzięki automatycznemu testowaniu interfejsu użytkownika niż testowi ręcznemu przy użyciu dowolnej techniki (koszt pisania / utrzymywania testów automatycznych jest niższy niż testowanie ręczne za każdym razem, ale jakość jest taka sama) Myślę, że powinieneś napisać artykuł. Przeczytałbym to! Widzę teraz tytuł: „Wytnij i wklej kod wygraną netto dla testowania interfejsu użytkownika!”


0

Naprawdę nie jest tak źle. W rzeczywistości, jeśli zauważysz, że niektóre wzorce kodu są używane bardzo często, a zmiany są bardzo rutynowe (takie jak kilka ciągów znaków lub wartości parametrów), możesz nawet napisać generator kodu, który generuje powtarzalny kod testowy na podstawie małego (- ish?) lista wejściowa zmieniających się wartości. Zrobiłem to (wygenerowałem kod testowy) wiele razy, używając plików wsadowych, skryptów SQLPlus, a nawet makr Excela (brzmi brzydko, ale zmienne dla różnych skryptów testowych były już w arkuszu kalkulacyjnym) i może to być świetna oszczędność czasu . Chodzi o to, że jeśli coś zmieni się w ogólnej strukturze powtarzalnego kodu przypadku testowego, możesz po prostu zregenerować wszystko, czego potrzebujesz.


0

Jest to to samo co większość innych odpowiedzi, ale w sposób zrozumiały dla nietechnicznego kierownika.

Wyobraź sobie następujący scenariusz błędu:

  • Ktoś dokonuje zmian w tabeli bazy danych, od których zależy wiele testów.
  • Konsekwencja: nagle 117 z 2933 automatycznych testów kończy się niepowodzeniem.

Co zrobisz?

  • (1) naprawić 117 testów?
  • (2) usuń 117 testów i przeprowadź je ponownie za pomocą nowej kopii i wklej. może to być łatwiejsze niż (1)
  • (3) refaktoryzuj testy w celu wyodrębnienia wspólnego kodu, aby w przyszłości trzeba było dostosować tylko jedną metodę (lub kilka) w celu naprawy testów (patrz odpowiedzi @pdr lub @Michael Brown)
  • (4) usuń 117 testów bez ich ponownej realizacji

Z mojego doświadczenia:

Wprowadzając zautomatyzowane zarządzanie testami, lubi się „kopiować i wklejać testy”: dostajesz wiele testów w krótkim czasie.

Po niektórych „scenariuszach błędów” zarządzanie preferuje (4), ponieważ naprawianie „testów kopiowania i wklejania” jest tak drogie.

Zrób to dobrze w pierwszej kolejności (3) nie będzie tak szybko, ale zwiększa szanse na przetrwanie testów

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.