Czy w TDD muszę najpierw napisać Test, czy interfejs?

23

Uczę się TDD przy użyciu c #, o ile wiem, test powinien kierować rozwojem , to znaczy najpierw napisać test zakończony niepowodzeniem po napisaniu minimalnego kodu, aby przejść test, a następnie dokonać refaktoryzacji.

Mówi się również, że „ Program do interfejsu, a nie implementacja ”, więc najpierw napisz interfejs . Tu zaczyna się moje zamieszanie. Jeśli najpierw piszę Interfejs, narusza to dwie rzeczy

  1. Kod napisany dla interfejsu nie jest sterowany przez test .

  2. Nie jest to absolutne minimum, oczywiście, że mogę napisać to za pomocą prostej klasy.

Czy powinienem zacząć od napisania testów interfejsu? bez żadnej implementacji, co zamierzam przetestować?

Jeśli to pytanie brzmi głupio przepraszam za to, ale jestem całkowicie zdezorientowany. Być może biorę rzeczy zbyt dosłownie.

k4vin
źródło
8
„Program do interfejsu” oznacza oddzielenie tego, czego potrzebujesz od fragmentu kodu, od tego, jak to się robi. To nie znaczy dosłownie używać interfacedo wszystkiego. A classzapewnia również interfejs, ponieważ można ukryć szczegóły implementacji w privatezmiennych.
Doval
@Doval, Tak, nie potrzebujesz interfejsu do wszystkiego, tylko to, co nazywa się contract. Może to być na przykład klasa abstrakcyjna, chociaż nie powinna to być wirtualna klasa / metoda, ponieważ nie powinno być możliwości jej utworzenia.
trysis
2
TDD mówi: „napisz test, który się nie powiedzie”. Niektórzy surowi TDDerzy twierdzą, że liczy się jako „niepowodzenie”, jeśli nie można skompilować testu, ponieważ typ danych, na którym działa, nie został jeszcze zadeklarowany.
Solomon Slow

Odpowiedzi:

29

Twoje pierwsze naruszenie („Kod napisany dla interfejsu nie jest sterowany przez test.”) Jest nieprawidłowy. Użyjmy trywialnego przykładu. Załóżmy, że piszesz klasę kalkulatora i piszesz operację dodawania. Jaki test możesz napisać?

public class CalculatorTest {
    @Test
    public void testAddTwoIntegers() {
        Calculator calc = new Calculator();
        int result = calc.add(2, 2)
        Assert.assertEquals(4, result);
    }
}

Twój test właśnie zdefiniował interfejs. To jest addmetoda, rozumiesz? addpobiera dwa argumenty i zwraca ich sumę. Możesz później ustalić, że potrzebujesz wielu kalkulatorów, i wyodrębnić (w tym przypadku) interfejs Java w tym czasie. Twoje testy nie powinny się wtedy zmienić, ponieważ przetestowałeś publiczny interfejs tej klasy.

Na poziomie bardziej teoretycznym testy są wykonywalną specyfikacją systemu. Interfejsy do systemu powinny być obsługiwane przez użytkowników tego systemu, a testy to pierwsza metoda definiowania interakcji.

Nie sądzę, że można oddzielić projekt interfejsu od projektu testowego. Definiowanie interakcji i projektowanie dla nich testów to ta sama operacja mentalna - kiedy wysyłam te informacje do interfejsu, oczekuję określonego rezultatu. Gdy coś jest nie tak z moim wejściem, oczekuję tego błędu. Możesz wykonać tę pracę projektową na papierze, a następnie napisać z niej swoje testy lub możesz wykonać je jednocześnie - to nie ma znaczenia.

Michael K.
źródło
2
+1 „ Nie sądzę, że można oddzielić projekt interfejsu od projektu testowego ” powinno być pogrubione, IMHO :)
Binary Worrier
Jeszcze łatwiej jest pokazać, że jeśli chcesz przetestować więcej niż jedną implementację funkcjonalności, mówi import XML i import CSV, możesz przetestować je dokładnie tą samą metodą z tego samego interfejsu, choć implementacja się zmieni. Co więcej, testy często zawierają pewne kpiny i dla tego interfejsu są konieczne.
Walfrat
Mówisz, że test nie musi się zmieniać, ale new Calculator()czy wdrożenie jest prawidłowe? Jeśli potrzebna jest nowa implementacja, może zrobiłbyś wtedy MultiplicationCalculator i musiałbyś zmienić test, aby użyć new AdditionCalculator()go, aby nadal mógł przejść? A może coś mi brakuje?
Steve Chamaillard,
3
@ SteveChamaillard Jasne, jeśli w twoim projekcie zmieniłeś nazwę klasy z Kalkulatora na Dodatkowy Kalkulator, test musiałby się zmienić, aby dopasować. Oczywiście, wykonując TDD, tak naprawdę by się to zdarzyło, że najpierw zmieniłby się test, a następnie nastąpiłaby zmiana klasy w celu zaliczenia testu.
Eric King,
5

Co robimy, kiedy piszemy interface? Piszemy kod, czy projektujemy?

Nie jestem fanem koncepcji Test Driven Design, ale uwielbiam Test Driven Development . Osobiście osiągnąłem najlepsze wyniki, projektując klasę z góry, projektując interfejs przed napisaniem testu. Nie liczę interfejsu jako kodu. Interfejs to projekt, który będę wdrażał za pomocą TDD. Prawdopodobnie zmieni się w trakcie pracy, ale jest to moja mapa drogowa (wraz z moją listą testów).

Przestanę, zanim zacznę mówić, ale mam nadzieję, że jest to pomocny sposób, aby o tym pomyśleć.

Gumowa kaczuszka
źródło
4

Czy w TDD muszę najpierw napisać Test, czy interfejs?

Wszystko zależy od tego, jak ortodoksyjni / religijni chcesz robić TDD .

Uczę się TDD

Ponieważ uczysz się, powinieneś eksperymentować, aby uzyskać osobisty przepływ pracy, który działa dla Ciebie.

  1. Jeśli chcesz to zrobić zgodnie z książkami , najpierw piszesz test, który oczywiście się nie powiedzie, ponieważ zaczynasz bez kodu. Następnie piszesz kod, aby test przeszedł pomyślnie. Jeśli tak się stanie, możesz przefakturować istniejący kod, ponieważ masz test, który zapewnia pewnego rodzaju siatkę bezpieczeństwa dla refaktoryzacji. Decyzja o użyciu interfejsu jest pewnego rodzaju refaktoryzacją.

  2. Poza TDD, czy nie: Pytanie, czy użyć interfejsu, czy nie, nie jest interesujące w pierwszej kolejności. Oczywiście, jeśli jesteś pewien, że masz różne zachowanie, które chcesz rozłożyć na kilka obiektów, warto pomyśleć o użyciu interfejsu: Na przykład, jeśli masz jakieś wyjście do różnych miejsc docelowych, warto wdrożyć je za pośrednictwem interfejs Writer i mają różne klasy danych wyjściowych ( FileWriter , Printer itp.). Chociaż pisanie do interfejsu jest powszechnym powiedzeniem , ale to nie znaczy: używaj interfejsu do wszystkiego . Czasami jest to jeden poziom pośredni za dużo. Btw. to samo dotyczy usług. Ale to inny temat.

  3. Z drugiej strony możesz opracować test napędzany w inny sposób: zaprojektuj kod pod kątem testowalności. Co oznacza, że ​​piszesz kod, który jest łatwy do przetestowania - chociaż testy są pisane później . Nie ma znaczenia, czy piszesz testy wcześniej, czy później, o ile i tak testujesz.

Thomas Junk
źródło
5
Nie mogę zgodzić się z ostatnim punktem „Nie ma znaczenia, czy piszesz testy wcześniej, czy później, o ile i tak testujesz”. Jeśli zdarzy ci się później napisać test, nie możesz być pewien, czy test sprawdza poprawność.
k4vin
4
Jak powiedziałem ... To zależy od tego, jak jesteś ortodoksyjny ...
Thomas Junk
2

TDD lub BDD oznaczałoby najpierw wykonanie interfejsów domeny, a następnie napisanie testów przeciwko nim według mojej interpretacji. implementacja interfejsu ma oczekiwane zachowanie.

wciąż testuje przed kodem, ponieważ interfejs nie zawiera logiki testowalnej, to jest struktura, na której piszesz test.

Zrobiłbym to w następujący sposób

  1. Napisz zachowanie półformalne (biorąc pod uwagę: Kiedy: Wtedy :)

  2. Napisz interfejs (do metody enkapsulacji zachowania hosta)

  3. Napisz Test, który identyfikuje (wprowadź dane, zadzwoń kiedy, przetestuj wtedy)

  4. Napisz / zmień beton (klasa implementująca interfejs), aby zdać test

użytkownik3721330
źródło
0

Nigdy nie pisz testów przed zaprojektowaniem interfejsów. Kiedy zastanawiasz się, jakie testy napisać (projekt testu), nie powinieneś jednocześnie projektować (architektura) aplikacji. Nie myśl o dwóch rzeczach jednocześnie. Czy słyszałeś o rozdzieleniu obaw? Dotyczy to nie tylko fizycznej struktury kodu, ale także procesu myślenia.

Zdecyduj, jak twoja aplikacja powinna być najpierw zaprojektowana. Oznacza to, że projektujesz swoje interfejsy i relacje między nimi. Dopóki tego nie zrobisz, nie powinieneś zaczynać myśleć o testach. Kiedy już wiesz, jakie masz interfejsy, możesz albo najpierw je utworzyć, a następnie napisać testy przeciwko nim lub najpierw napisać testy, a następnie je utworzyć. W tym drugim przypadku oczywiście nie będziesz w stanie skompilować testów. Nie widzę żadnej szkody ani żadnego naruszenia filozofii TDD w tworzeniu interfejsów przed testami.

Nissim Levy
źródło
uzasadnienie w górnej odpowiedzi wygląda bardziej przekonująco: „Nie sądzę, że można oddzielić projekt interfejsu od projektu testowego. Definiowanie interakcji i projektowanie testów dla nich to ta sama operacja umysłowa - kiedy wysyłam te informacje do interfejsu, oczekuję określonego wyniku . Kiedy coś jest nie tak z moim wejściu, ja się spodziewać tego błędu ...”
gnat
@gnat Wydaje mi się, że Nissam odnosi się tutaj do interfacesłowa kluczowego C # , a nie ogólnego terminu „interfejs”.
RubberDuck,
@RubberDuck Też tak myślę i uważam, że jest to złe podejście. „Dopóki tego nie zrobisz, nie powinieneś zaczynać myśleć o testach ...” Jak napisałem, rozumowanie w górnej odpowiedzi wygląda bardziej przekonująco, zarówno w ogólnym znaczeniu interfejsu, jak i konkretnego słowa kluczowego
gnat
Wystarczająco uczciwa @gnat, która nie była jednoznaczna z twojego komentarza. Osobiście zgadzam się z Nissam tutaj. Uważam, że powstrzymuje ludzi od używania TDD jako wymówki, by w ogóle nie projektować. YMMV.
RubberDuck,
-2

Można pisać interfejs / kod / test w tym samym czasie, o ile ich włączenie do projektu jest atomowe.

Chyba że twój szef jest religijny na temat TDD, w takim przypadku prawdopodobnie musisz napisać pusty interfejs -> test -> minimalny kod (bezcelowy krok) -> więcej testów -> więcej bezsensownego kodu -> więcej testów -> w końcu napisać prawdziwy kod > gotowe.

alternatywny
źródło