Metodologia: Pisanie testów jednostkowych dla innego programisty

28

Myślałem o rozwoju oprogramowania i pisaniu testów jednostkowych. Mam następujący pomysł:

Załóżmy, że mamy pary programistów. Każda para odpowiada za część kodu. Jeden z pary implementuje funkcję (pisanie kodu), a drugi pisze dla niej testy jednostkowe. Testy są pisane po kodzie. Według mnie pomagają sobie nawzajem, ale działają raczej osobno. Idealnie byłoby pracować na dwóch podobnych rozmiarach, a następnie wymienić na przygotowanie do testu.

Myślę, że ten pomysł ma kilka zalet:

  • testy są pisane przez kogoś, kto może zobaczyć więcej o implementacji,
  • praca powinna być wykonywana niewiele szybciej niż programowanie parami (dwie funkcje jednocześnie),
  • zarówno testy, jak i kod ma za to osobę odpowiedzialną,
  • kod jest testowany przez co najmniej dwie osoby, oraz
  • być może wyszukiwanie błędów w kodzie napisanym przez osobę, która testuje kod, dałoby specjalną motywację do pisania lepszego kodu i unikania skracania narożników.

Być może dobrym pomysłem jest dodanie kolejnego programisty do przeglądu kodu między kodem a testowaniem.

Jakie są wady tego pomysłu? Czy jest już opisana jako nieznana mi metodologia i stosowana w rozwoju oprogramowania?

PS. Nie jestem zawodowym kierownikiem projektu, ale wiem coś o procesach rozwoju projektu i znam kilka najpopularniejszych metodologii - ale ten pomysł nie wydaje mi się znajomy.

franis
źródło
17
Po prostu opisujesz kontrolę jakości na poziomie jednostki. Jeśli masz pary ludzi, którzy nad czymś pracują, czy próbowałeś programowania w parze z TDD?
jonrsharpe
9
Działałoby to lepiej, gdyby autor testów najpierw wykonał testy (napisał kod szkieletowy), a drugi zaimplementował tę funkcjonalność. Pierwszy z nich miałby kontrolę nad projektem, a drugi podnosiłby ciężki ładunek. To może działać dobrze, jeśli pierwszy wie, co robi, a drugi nie ma nic przeciwko ciągłym podążaniu za nim. Nie znam nazwy tego sposobu współpracy. Powiedziałbym ... odbierz to! Zacznij nazywać ten rozwój Franiis.
Martin Maat
14
Ta krytyka nie ma sensu, a twoja sugestia nie rozwiązuje tego problemu.
jonrsharpe
5
@franiis Widziałem, jak koledzy piszą assert truejako testy i nazywają to dniem, ponieważ każdy test mijał. Brakowało jednego ważnego kroku: testy powinny zakończyć się niepowodzeniem jako pierwsze i należy je wykonać, zmieniając kod, a nie testy.
Eric Duminil
6
@franiis TDD jest zbudowany wokół iteracji. Napisz test negatywny. Napisz kod, który sprawi, że test będzie zielony. Refaktor. Napisz test negatywny. Napisz kod, który sprawi, że test będzie zielony. Refaktor. Wygląda na to, że brakuje Ci części „powtarzaj, dopóki nie przeprowadzisz testów obejmujących wszystkie Twoje wymagania”. Ale największym problemem, jaki wydajesz się mieć, jest to, że „testy” są postrzegane jako coś, co musisz mieć, ponieważ ktoś tak powiedział, zamiast testów będących przydatnym narzędziem dla programistów . Jeśli nie możesz zmusić ludzi do dbania o jakość (i poprawność) ich kodu, to jest twój problem i od tego powinieneś zacząć.
Luaan,

Odpowiedzi:

30

Ogólne podejście polegające na stosowaniu par do dzielenia wysiłków związanych z pisaniem kodu produkcyjnego i pisaniem powiązanych testów jednostkowych nie jest rzadkie. Już wcześniej osobiście sparowałem w ten sposób z dobrym skutkiem. Jednak ścisła granica między osobą piszącą kod produkcyjny a osobą piszącą kod testowy niekoniecznie musi dać wyniki.

Kiedy zastosowałem podobne podejście, para zaczyna od rozmowy i wspólnego zrozumienia problemu. Jeśli używasz TDD, możesz zacząć od kilku podstawowych testów. Jeśli nie używasz TDD, być może zaczniesz od definicji metody. Odtąd obaj członkowie pary pracują zarówno nad kodem produkcyjnym, jak i testowym, przy czym jedna osoba koncentruje się na każdym aspekcie, ale mówi o sposobach ulepszenia kodu produkcyjnego, a także kodu testowego.

Nie widzę korzyści z nadania każdej parze dwóch funkcji. To, co byś skończył, to coś, co przypomina TDD dla niektórych funkcji, a coś, co nie jest dla innych funkcji. Tracisz koncentrację. Nie zyskujesz korzyści z wzajemnej oceny w czasie rzeczywistym. Nie zyskujesz żadnej z głównych zalet parowania.

Praktyka programowania par nie polega na szybkości, ale na jakości. Więc próba użycia zmodyfikowanej techniki napędzanej szybszym biegiem jest sprzeczna z naturą. Budując oprogramowanie wyższej jakości poprzez równoległe przeglądanie kodu i opracowywanie testów, oszczędzasz czas na dalszych etapach, ponieważ co najmniej dwie osoby mają wiedzę o każdej zmianie i eliminujesz (lub redukujesz) cykle oczekiwania na przegląd i testowanie.

Thomas Owens
źródło
Dziękuję, mój pomysł zakłada, że ​​obie funkcje są rozwijane w ten sam sposób (ale programiści wymieniają role) - tylko w celu wyjaśnienia, a nie obrony sensu tej koncepcji. Podoba mi się twoja odpowiedź i twój nacisk na szybkość w porównaniu z jakością.
franiis
Z mojego doświadczenia wynika, że ​​koszty przeróbki przewyższają korzyści płynące z tego podejścia. Wolałbym, żeby para oddała te obowiązki za pomocą „ping-ponga” lub innej metody.
neontapir
3
Praktyka programowania par nie polega na szybkości, ale na jakości. Para TDD dotyczy jakości, która zapewnia szybkość realizacji, co obniża koszty rozwoju. Po prostu nasza branża uczy się, co masoni znają z millenialsów: twoja ściana zostanie zbudowana lepiej w krótszym czasie, przy mniejszym wysiłku i koszcie, jeśli poświęcisz trochę czasu na ustawienie linii sznurka i zasadę masonów, a następnie połóż cegły, niż jeśli kładziesz cegłę, a następnie próbujesz dostosować się za pomocą poziomnicy i młotka. I uzyskaj pomoc w różnych sprawach.
Laurent LA RIZZA
@LaurentLARIZZA To wydaje się poprawne. Przypuszczam, że lepszym sposobem na powiedzenie tego byłoby: „Praktyka programowania par nie polega obecnie na prędkości, ale na jakości i szybkości w przyszłości”. Zdecydowanie jest to wybiegająca w przyszłość praktyka znajdowania problemów wcześniej, zwiększania solidności pracy i dzielenia się wiedzą w celu burzenia silosów. Wszystkie z nich mają teraz koszt, który często zapłaci nagrody w przyszłości.
Thomas Owens
@ThomasOwens: Koszt jakości jest tylko postrzegany, a nie rzeczywisty. Po przejściu testu (i uporządkowaniu kodu) scenariusz opisany przez test jest zakończony i zabezpieczony, a Ty zyskujesz pewność, że zadziała zgodnie z oczekiwaniami. Zrobione i możesz przejść dalej. Jeśli przejdziesz dalej bez pewności, że kod działa, po prostu zaakceptowałeś dług, który będziesz musiał wykonać później. Koszt długów, a nie brak długów. Chodzi mi o to, że „przyszłość”, o której mówisz, jest zaraz po przejściu pierwszego testu.
Laurent LA RIZZA
37

Głównym problemem związanym z Twoim pomysłem jest to, że nie możesz po prostu pisać testów dla dowolnego kodu. Kod musi być testowalny.

Tzn. Musisz mieć możliwość wstrzykiwania próbnych prób, oddzielić bit, który chcesz przetestować, dostęp do stanu, który został zmieniony i wymaga potwierdzenia itp.

O ile nie masz szczęścia lub nie napisasz testu jako pierwszy, szanse na napisanie testu oznaczają trochę przepisanie kodu. Co, jeśli nie jesteś osobą, która pisze kod, będzie oznaczało opóźnienie, spotkania, refaktoryzację itp.

Ewan
źródło
Dzięki. ale powszechna krytyka TDD polega na tym, że kod jest czasami / często zapisywany, aby testy były „zielone” - nie, żeby było dobre. Jeśli testy nie testują jakiegoś aspektu kodu, można go pominąć w kodzie. Późniejszy test może pomóc (akceptuję, że po napisaniu kodu mogą być wymagane pewne zmiany, ale programiści powinni nauczyć się pisać więcej testowalnego kodu w przyszłości).
franiis
1
@franiis na pewno, głównym problemem nie jest to, że piszesz testy później, jest to kombinacja robienia tego i nie bycia tą samą osobą, która napisała kod.
Ewan
ale przy użyciu np. programowania w parach byłoby to mniej czasochłonne. Jeśli dwóch programistów pracuje na jednym terminalu, nie mogą w żaden sposób jednocześnie pracować nad dwiema funkcjami, a mój pomysł powinien na to pozwolić (nawet w ograniczonym zakresie). Spotkania dla 2-osobowego mikro-zespołu nie powinny być prawdziwym obciążeniem.
franiis
25
@franiis: „Jeśli testy nie testują jakiegoś aspektu kodu, można go pominąć w kodzie”. - O to chodzi. Testy są kodowaniem wymagań w postaci wykonywalnych przykładów. Jeśli nie ma na to testu, nie ma na to wymagań i nie powinno być dla niego kodu .
Jörg W Mittag
3
Drugą stroną tego, co powiedział @ JörgWMittag, byłoby: jeśli twoje testy „nie testują jakiegoś ważnego kodu”, musisz je naprawić. Będzie to tak samo prawdziwe w twoim systemie, jak w tradycyjnym TDD.
bta
15

Główny problem, który widzę tutaj, na poziomie jednostki, kiedy piszę kod, chcę go skompilować, uruchomić i natychmiast usunąć najbardziej oczywiste błędy - nawet jeśli kod jest niekompletny i wiem, że jednostka, funkcja lub funkcja to tylko częściowo wdrożony. A do uruchomienia kodu jednostki potrzebuję jakiegoś programu wywołującego implementację, zwykle test jednostkowy lub przynajmniej częściowy test jednostkowy. Niekoniecznie jest to „styl TDD według książki”, taki test można napisać przed lub przed testowanym kodem.

Gdy jedna wersja mojej jednostki jest „kompletna” i wolna od wszelkich błędów, które mogę znaleźć w ten sposób, wtedy sensowne jest przekazanie jej drugiej osobie i pozwolić jej / jej na napisanie dodatkowych testów jednostkowych lub przejrzenie mojego kodu . Ale dla mnie nie ma sensu przekazywać go, gdy tylko kompilator nie wyświetli ostrzeżeń, to zdecydowanie za wcześnie, na wypadek, gdybym wiedział, że muszę szczegółowo wyjaśnić testerowi rzeczy, które nie działają „jeszcze” lub działają inaczej za dwie godziny, odkąd wciąż pracuję nad tym fragmentem kodu. Niezbędny narzut komunikacyjny na tym poziomie szczegółowości nie przyniosłby IMHO korzyści.

Więc tak, mający drugi dev pisanie dodatkowych testów jednostkowych sens, ale nie do pisania testów jednostkowych wyłącznie .

Doktor Brown
źródło
7

Wydaje się, że może wystąpić jedna z następujących sytuacji - wszystkie są niepożądane:

Zamieszanie

Jak wskazał Ewan, CUT może wymagać zmiany, aby umożliwić testowanie. Powód zmiany nie zawsze jest oczywisty dla dewelopera (i może powodować nieporozumienia) i właśnie dlatego testy są pisane jako pierwsze.

Twierdzenie

Deweloper A mógł ukończyć swój kod i chcieć go przetestować. Deweloper B może również się rozwijać i dlatego może być niechętny, aby zaparkować swój kod, aby wziąć udział w testach jednostkowych.

Przełączanie kontekstu

Nawet jeśli programista B chce odłożyć programowanie na później, aby przetestować kod napisany przez programistę A - zmiana aktywności wiąże się z pewnymi kosztami.


Od dziesięcioleci przyjęto, że podwojenie siły człowieka nie skraca o połowę czasu rozwoju. Biorąc pod uwagę czynniki, które przedstawiłem powyżej, trudno jest zobaczyć, jak to ustawienie poprawiłoby sytuację.

Robbie Dee
źródło
4

W połączeniu z programowaniem par i TDD nazywa się to Wzorem Ping Pong :

  • A pisze nowy test i widzi, że się nie udaje.
  • B implementuje kod potrzebny do zdania testu.
  • B pisze następny test i widzi, że się nie udaje.
  • Implementuje kod potrzebny do zaliczenia testu.

I tak dalej. Refaktoryzacja odbywa się za każdym razem, gdy zaistnieje taka potrzeba.

Ale wydaje się, że proponujesz, aby oba programatory kodowały na różnych komputerach. Wykonanie tego osobno wymagałoby specyfikacji na bardzo niskim poziomie. Jest to sprzeczne z metodologiami zwinnymi. Każda zmiana wymagałaby koordynacji. W TDD wykonujesz desing na niskim poziomie w locie i nie stanowi to problemu. Zakładam, że twoje podejście wymagałoby wcześniej zakodowania szkieletów.

W każdym razie: możesz się wiele nauczyć, testując nowe sposoby robienia rzeczy, nawet jeśli nie są w 100% wydajne. Możesz to przetestować i podzielić się swoimi doświadczeniami z życia

Borjab
źródło
3

Spóźniam się na to przyjęcie, ale myślę, że mam coś do dodania.

Czy jest już opisana jako nieznana mi metodologia i stosowana w rozwoju oprogramowania?

Opisujesz Peer kontrolne .

Załóżmy, że mamy pary programistów.

Ach, dobre stare programowanie par .

Każda para odpowiada za część kodu. Jeden z pary implementuje funkcję (pisanie kodu), a drugi pisze dla niej testy jednostkowe. Testy są pisane po kodzie. Według mnie pomagają sobie nawzajem, ale działają raczej osobno.

To nie jest programowanie w parach.

Idealnie byłoby pracować na dwóch podobnych rozmiarach, a następnie wymienić na przygotowanie do testu.

To zdecydowanie testy rówieśnicze. Oto artykuł ACM na ten temat . Zrobiłem to. Pracowałem tam, gdzie była to formalna część procesu wzajemnej oceny . Jest to pomocne, ale z pewnością nie jest pierwszą linią testów, a na pewno nie jest to klasyczne programowanie w parach.

Inną nazwą tego jest Testowanie Whitebox . Chociaż ta definicja nie dotyczy samego tego, kto przeprowadza testy, a także faktu, że tester widzi wewnętrzne funkcjonowanie testowanej rzeczy, w przeciwieństwie do testowania w Czarnej skrzynce, gdzie widzą tylko to, co wchodzi i co wychodzi Czarna skrzynka jest zwykle tym, co robi QA.

Pierwsza linia testów spoczywa mocno w rękach programisty. Jeśli nie, to prosisz mnie, abym nie testował mojego kodu, czego zdecydowanie odmawiam. Testuję swój kod, odkąd skończyłem 10 lat. Mogłem wtedy nie testować fantazyjnych testów jednostkowych, ale mój kod został przetestowany. Był testowany przy każdym uruchomieniu.

Od peer-testera oczekuję testów, które dodają się do moich testów. Testy, które w znacznym stopniu wyjaśniają problemy, które peer znalazł przy kodzie podczas jego przeglądu. Wyrażenie tych problemów za pomocą automatycznego testu ułatwia zrozumienie ich znaczenia. Rzeczywiście, przeprowadziłem techniczne rozmowy z rówieśnikami, którzy po prostu nie mogli zrozumieć, o co mi chodzi, a potem zrozumiałem, że najlepszym sposobem na pokazanie im, że jest problem, jest napisanie testu jednostkowego. To jest Peer Testing.

Teraz, jeśli chcesz dać mi testy napisane przed napisaniem mojego kodu w porządku. Nie ma to jak dokument wymagań, który jest tak formalny, że się kompiluje.

candied_orange
źródło
Dziękuję za odpowiedź i wskazując mi na testy rówieśnicze (przeczytam o tym).
franiis
1

Od kilku lat wykonuję DDT (testy rozwojowe, czyli testy po kodzie), programowanie par i TDD z czerwono-zielonym refaktorem. Aby odpowiedzieć na twoje twierdzenia punkt po punkcie:

testy są pisane przez kogoś, kto może zobaczyć więcej o implementacji

Osoba pisząca testy musi znać implementację tak dokładnie, jak to możliwe, aby pisać testy z dobrym zasięgiem bez nadmiernego testowania. Klasycznym przykładem tego jest testowanie z trzema wejściami, kiedy dwa potwierdzą to, co próbujesz przetestować. Mimo że potrafią na powierzchni zapoznać się z kodem po jego odczytaniu, nie będą w stanie zrozumieć dokładnie, przez co przeszedł pierwotny programista, aby przejść do obecnego stanu. Będą więc mieli mniej niż optymalne zrozumienie kodu.

praca powinna być wykonywana niewiele szybciej niż programowanie parami (dwie funkcje jednocześnie)

Nie rozumiem, dlaczego tak mówisz. Podczas gdy ktoś pisze testy, nie pracuje nad nowymi funkcjami. Nie można magicznie podwoić czyjejś zdolności do pracy, dając jej dwa różne rodzaje pracy. Z mojego doświadczenia wynika, że ​​pisanie testów jest generalnie trudniejsze niż pisanie kodu produkcyjnego, więc zdecydowanie nie można produktywnie i odpowiedzialnie pracować nad testami jakiegoś kodu podczas pisania innej funkcji.

zarówno testy, jak i kod mają odpowiedzialną za to osobę

Po pierwsze, testy kodem. Dla kodu testu biznesowego jest prawie tak samo ważny jak kod produkcyjny, ponieważ umożliwia firmie zmianę oprogramowania bez obaw. Po drugie, nie różni się niczym od jednej osoby piszącej testy i kod produkcyjny, a nawet pary piszącej obie.

kod jest testowany przez co najmniej dwie osoby

Nie, jest testowany tylko przez osobę, która pisze test. O ile nie chcesz poświęcić więcej czasu na testowanie, w takim przypadku po co zatrzymywać się na drugiej?

być może wyszukiwanie błędów w kodzie napisanym przez osobę, która testuje kod, dałoby specjalną motywację do pisania lepszego kodu i unikania skracania narożników.

Programiści (nawet starsi) mają bardzo różne pomysły, co stanowi „dobry” kod. Wycinanie narożników przez jedną osobę jest jak najbardziej poprawnym sposobem na jak najszybszy dostęp do działającego kodu. To przepis na winę i na grę w system.

Refaktor TDD czerwono-zielony (w rzeczywistości pisząc pojedynczy test przed napisaniem kodu produkcyjnego, uruchamiając go, widząc, że się nie udaje, modyfikując tylko kod produkcyjny , ponownie uruchamiając test, widząc, że się powiódł, a następnie refaktoryzując, nie pomijając ani nie zamieniając żadnego z te kroki) i sprawdzanie kodu działa.

10b0
źródło
Byłoby to szybsze (przypuszczalnie), ponieważ nie ma dwóch osób wykonujących „tę samą pracę” - każda z nich robi swoje, a następnie zamienia się w połowie.
Jacob Raihle
@JacobRaihle Parowanie to nie dwie osoby rozwijające się obok siebie bez komunikacji. To byłyby dwie osoby wykonujące tę samą pracę. Parowanie jest naprawdę wydajne, ponieważ dwie osoby współpracują przy jednym zadaniu. Z mojego doświadczenia wynika, że ​​rozwój jest prawie tak szybki, jak w przypadku indywidualnych programistów (tj. Pary wykonują pracę dwa razy szybciej), powstałe oprogramowanie ma znacznie wyższą jakość i wiedza została udostępniona.
l0b0
Próbuję wyjaśnić uzasadnienie, że „praca powinna być trochę przyspieszona”, co wydawało się wprowadzać w błąd. Parowanie jest zwykle wolniejsze z mojego doświadczenia, choć nadal uważam, że jest tego warte (lepiej zarówno w przypadku pracy indywidualnej, jak i przekazywania testów PO). Jeśli jest to dla ciebie szybsze, tym lepiej.
Jacob Raihle
1

Myślę, że ten pomysł ma kilka zalet:

Le'ts przebiegają przez nie jeden po drugim.

testy są pisane przez kogoś, kto może zobaczyć więcej o implementacji,

Masz na myśli, że pierwszy programista poświęcił czas na napisanie implementacji, co do której nie jest pewien, czy działa. Potem przychodzi inny programista i pisze testy, opierając swoje rozumowanie na kodzie, nikt nie wie, czy jest poprawny, i ma nadzieję, że przyniesie on taktyczną przewagę w porównaniu do pisania testów tylko w odniesieniu do tego, co powinien zrobić kod. Jeśli implementacja jest niepoprawna, moim zdaniem przyniesie zerową pomoc w pisaniu testów.

praca powinna być wykonywana niewiele szybciej niż programowanie parami (dwie funkcje jednocześnie)

Po zakończeniu początkowego rozwoju przez obu programistów nikt nie wie, czy którykolwiek z ich kodów jest poprawny. To wciąż pozostaje do sprawdzenia, nikt nie może odznaczać nikogo, jak to zrobione, i nikt nie może przewidzieć, kiedy będzie to zrobione. Porównaj to z TDD: najpierw piszesz test, potem test kończy się niepowodzeniem, a następnie przekazujesz z kodem. To kod obsługujący coraz więcej scenariuszy. To ruch do przodu.

Jeśli sprawisz, że będą postępować równolegle, kod, który może być ponownie wykorzystany w obu funkcjach, zostanie napisany dwukrotnie i będzie kosztował dwa razy więcej.

zarówno testy, jak i kod ma za to osobę odpowiedzialną,

Sprawdź własność kodu zbiorowego, zgodnie z propozycją XP. Będziesz mieć jeszcze więcej osób odpowiedzialnych za kod. Jeśli Twoim celem jest dzielenie się wiedzą między programistami, dlaczego próbujesz je segregować?

kod jest testowany przez co najmniej dwie osoby

Również z parą TDD. Podczas parowania obie osoby muszą uzgodnić, że napisany kod jest odpowiedni lub go nie napisać. Jeśli to doprowadzi do walki, niektórzy ludzie w zespole mają problem z niewłaściwym rozmieszczeniem ego.

być może wyszukiwanie błędów w kodzie napisanym przez osobę, która testuje kod, dałoby specjalną motywację do pisania lepszego kodu i unikania skracania narożników.

Poszukiwanie błędów oznacza, że ​​w pewnym momencie tolerowałeś ich dostanie się. Jeśli dostali się, byli niezauważeni. Odmowa najpierw napisania testu daje licencję na błędy, aby się dostać.

Cięcie narożnika może być niezamierzone. Do tego służy programowanie parowe. Każdemu członkowi pary należy pouczyć, aby obowiązek nie pozwalał drugiemu skracać narożników, bo cóż, wszyscy to robimy. Wymaga to pozostawienia dumy w szafie i zabrania jej z powrotem po wyjściu z biura. Jeśli spodziewasz się, że twoi ludzie będą niezachwianie rygorystyczni, nie bierzesz pod uwagę zwykłej sytuacji i nie przygotowuje się na porażkę.

XP mówi wprost, że wszystkie praktyki XP są wykonywane w celu wzajemnego wzmacniania się poprzez wzajemne usuwanie wad. Nie powinieneś słuchać krytyki jakiejkolwiek praktyki XP oddzielonej od innych. Żadna praktyka nie jest idealna, TDD nie jest doskonała, programowanie w parach nie jest idealne, własność wspólnego kodu nie jest doskonała, ale wszystkie one pokrywają się nawzajem.

Laurent LA RIZZA
źródło