Czy używasz TDD? oraz pytania do TDD.

0
slsy napisał(a):
Riddle napisał(a):

Not true, ja tak piszę praktycznie wszystkie programy - prywatne i w pracy. Tak również napisałem edytor na 4p.

Załóżmy, że piszesz parser JSONa. Uznałeś, że pierwszym przypadkiem (albo jednym z pierwszych) będzie sparsowanie prostego obiektu np. {"a": "3"}. Następnym krokiem będą zagnieżdżone obiekty tj. {"a": {"b": 3}}. Okazuje się, że pierwszy przypadek zaklepałeś jako prosty parser w stylu języka regularnego tj. jedzie od lewej do prawej i tyle, bo jest najprościej i najszybciej a testy przechodzą. Niestety do drugiego przypadku trzeba użyć parsera do gramatyki bezkontekstowej; potrzebujemy jakiegoś stosu lub rekurencji i okazuje się, że pierwotny kod musi pójść na śmietnik. Pytanie: czy napisałbyś taki kod wiedząc, że nie ma on nic wspólnego z ostateczną wersją? Właśnie tak wygląda wspomniany już wcześniej przykład ze stosem: fajnie, ale bez sensu

Tak, zrobiłbym tak. Błąd który popełniasz to nie zrozumienie zasady "minimal code pass the test".

A minimum które wystarczy żeby przeszedł Twój przypadek to jest return singletonMap("a", 3);.

Widać że nie stosujesz TDD bo myślisz o zbyt dużych zmianach implementacji w porównaniu do zbyt małej ilości testów.

Riddle napisał(a):

To o czym piszesz to zjawisko w którym masz monolit, ale próbujesz go testować jak graf. Nie ma tak. Albo masz kod jak graf i testujesz go jak graf, albo masz monolit i testujesz go jak monolit. There is no middle ground.

Za bardzo nie rozumiem twojej nomenklatury tj. czym jest monolit w tym kontekście. Ja zakładam, że kod może się rozwinąć w dowolnym kierunku i część, która nie była przetestowania w izolacji może się taką stać. Przykładowo mamy REST API, ale ktoś uznał, że chce gRPC albo GraphQL. Logika generalnie jest taka sama, różni się tylko kontroler. To co możemy zrobić:

  • traktujemy kontroler jako osobny moduł i testujemy z mockami. Działa, ale mało testujemy
  • implementujemy takiego GraphQLa poprzez użycie RESTa. Stare testy testują RESTa jak należy. Nowe testy testują jakieś proste przypadki, żeby upewnić się, że ta prosta translacja REST -> GraphQL działa. Jest ok, ale może być problem z performancem. Dodatkowo, możemy mieć w planach wywalenie RESTa (np. za rok a GraphQL zostanie) przez co w takim podejściu będziemy mieli na wieczność kod, który nie jest potrzebny
  • piszemy osobne zestawy testów dla obu protokołów: zadziała, ale upierdliwe i spada nam zwinność, bo każda zmiana w jednym miejscu afektuje dwa flow. Do tego te testy będą bardzo podobne
  • piszemy testy dla wspólnej logiki. Testy RESta zostają lub zostają uszczuplone, bo ciężar został przeniesiony niżej. Nowe testy GraphQL są prostsze niż stare dla RESTa, bo teraz logika jest wspólna i testujemy logikę a nie endpointy HTTP.

Nie mam czasu żeby to tłumaczyć tutaj, ale jeszcze bardziej pokazałeś że nie wiesz jak odpowiednio dzielić kod na moduły.

Wszystkie problemy które opisałeś da się rozwiązać, jeśli tylko stosuje się TDD w odpowiedni sposób. Możesz nie wiedzieć jaki to sposób, jeśli nie stosujesz TDD, ale to nie znaczy że takiego nie ma.

Już kończę rozmowę tutaj. Przeczytaj dwie książki które podlinkowałem, podałeś dobre pytania, ale na każde z nich jest odpowiedź, jeśli tylko masz wystarczająco dużo doświadczenia w tej metodyce. Mi zajęło spokojnie z 4 lata praktyki, żeby się tego nauczyć.

Jeśli chcesz kontynuować temat, to załóż proszę wątek "Jak napisać parser JSON'a w TDD", to chętnie pomogę, pokażę tricki, wytłumaczę jak to można zrobić dobrze.

2
Riddle napisał(a):

A minimum które wystarczy żeby przeszedł Twój przypadek to jest return singletonMap("a", 3);.

Inne przypadki

{}
{"a": "b"}
{"a",} -> błąd
{"a": "b"}
{"a": "b", } -> błąd bo przecinek
{"a": "b", "a": "b"} -> błąd bo duplikat
{"a": } -> błąd bo nie ma niczego po :

IMO takiego kodu nie da się przetestować bez prawdziwego parsera.

Riddle napisał(a):

Wszystkie problemy które opisałeś da się rozwiązać, jeśli tylko stosuje się TDD w odpowiedni sposób. Możesz nie wiedzieć jaki to sposób, jeśli nie stosujesz TDD, ale to nie znaczy że takiego nie ma.

Już kończę rozmowę tutaj. Przeczytaj dwie książki które podlinkowałem, podałeś dobre pytania, ale na każde z nich jest odpowiedź, jeśli tylko masz wystarczająco dużo doświadczenia w tej metodyce. Mi zajęło spokojnie z 4 lata praktyki, żeby się tego nauczyć.

Jeśli chcesz kontynuować temat, to załóż proszę wątek "Jak napisać parser JSON'a w TDD", to chętnie pomogę, pokażę tricki, wytłumaczę jak to można zrobić dobrze.

Mógłbyś się bardzie wysilić? Argumenty w stylu "nie znasz się" i "to jest w książce" pasują do innego polskiego forum o tematyce technicznej.

Riddle napisał(a):

Nie mam czasu żeby to tłumaczyć tutaj, ale jeszcze bardziej pokazałeś że nie wiesz jak odpowiednio dzielić kod na moduły.

Mógłbyś rozwinąć? Podejście z rozdzielonym kodem pomiędzy logikiem i serwisem to chyba standard a ja mówię o takie sytuacji

0
slsy napisał(a):

IMO takiego kodu nie da się przetestować bez prawdziwego parsera.

Da się, myślę że żeby te przypadki testowe przeszły, wystarczyłoby 3-5 linijek kodu.

Mówię Ci: załóż wątek "Jak napisać parser JSON'a w TDD" to Ci pokażę. Tutaj nie zaśmiecajmy tego.

Sam napisałem kiedyś parser XML'a w taki sposób, iteratywnie dodając logikę i testy, i pierwsze 100 testów to był żart, a nie parser. Więć wiem co mówię - da się. Idzie szybko, bezpiecznie, jeden test za drugim, i na końcu masz minimum kodu spełniającego testy i 99-100% coverage.

Rozumiem, że może Ci być ciężko sobie to wyobrazić, bo jeszcze tego nie umiesz, ale to nie znaczy że metodyka TDD jest zła. Może po prostu Ty jeszcze jej nie umiesz.

Riddle napisał(a):

Mógłbyś rozwinąć? Podejście z rozdzielonym kodem pomiędzy logikiem i serwisem to chyba standard a ja mówię o takie sytuacji

Jeśli faktycznie je rozdzielisz to tak. Ale z opisu tego co mówisz, nie były faktycznie rozdzielone.

Wątek jest o tym czy używa się TDD i pytania na jego temat. Nie zamieniaj tego wątku w prywatę o parserze JSON'ów, załóż osobny wątek.

1

Nie lubię TDD, bo mimo to, że wiem co chcę osiągnąć i mogę to wymaganie wyrazić testem jednostkowym, to nie wiem jak chcę to zakodować i zawsze wolę sobie napisać kod. Po prostu uważam, żeby kod był testowalny tj. rozbity odpowiednio na klasy i metody oraz żeby metody zawsze zwracały wartość, nigdy void.

3
NeutrinoSpinZero napisał(a):

Nie lubię TDD, bo mimo to, że wiem co chcę osiągnąć i mogę to wymaganie wyrazić testem jednostkowym, to nie wiem jak chcę to zakodować i zawsze wolę sobie napisać kod.

Testy sprawdzają wejście i wyjście, więc nie ma znaczenia jak to zakodujesz, dopóki kontrakt (czy nawet sygnatura metody) się zgadza.

Po prostu uważam, żeby kod był testowalny tj. rozbity odpowiednio na klasy i metody oraz żeby metody zawsze zwracały wartość, nigdy void.

No i właśnie w tym testy pomagają. Do metod void (łatwo sensownych) testów nie napiszesz.

0
somekind napisał(a):

No i właśnie w tym testy pomagają. Do metod void testów nie napiszesz.

Potrzymaj mi piwo :P


[Fact]
public void Should_call_foo_method()
{
  ...
  myServiceMock.Verify(x => x.Foo()); // gdzie metoda Foo jest void
}
 

:D

2
somekind napisał(a):
NeutrinoSpinZero napisał(a):

Nie lubię TDD, bo mimo to, że wiem co chcę osiągnąć i mogę to wymaganie wyrazić testem jednostkowym, to nie wiem jak chcę to zakodować i zawsze wolę sobie napisać kod.

Testy sprawdzają wejście i wyjście, więc nie ma znaczenia jak to zakodujesz, dopóki kontrakt (czy nawet sygnatura metody) się zgadza.

Jak implementacja bierze dane z d.. zamiast z parametrów to trudniej napisać test jednostkowy. Przykład w Javie to sławne new Date() Czy teraz po nowemu LocalDateTime.now()

0

To jak stosowac TDD kiedy piszemy cos od nowa albo sami zostalo juz dobrze wyjasnione przez @Riddle. Ciekawi mnie co jesli pracujemy w dojrzalym projekcie i musimy dopisac nowy endpoint albo co gorsza zmodyfikowac jakis feature?

3

Jak implementacja bierze dane z d.. zamiast z parametrów to trudniej napisać test jednostkowy. Przykład w Javie to sławne new Date() Czy teraz po nowemu LocalDateTime.now()

Bierzesz i robisz TimeProvider i YOLO.

1
Seken napisał(a):

To jak stosowac TDD kiedy piszemy cos od nowa albo sami zostalo juz dobrze wyjasnione przez @Riddle. Ciekawi mnie co jesli pracujemy w dojrzalym projekcie i musimy dopisac nowy endpoint albo co gorsza zmodyfikowac jakis feature?

Piszesz test który failuje, dopisujesz cześć featurea, test przechodzi, powtórz.

1 użytkowników online, w tym zalogowanych: 0, gości: 1