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.
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.