[ten post jest częścią mojego minicyklu o testach, pełna lista postów: tutaj]
Na tak postawione pytanie aż chciałoby się odpowiedzieć: “testować wszystko, you fool!“. Życie uczy jednak, że takie podejście jest bardzo niepraktyczne i na dłuższą metę nie ma sensu. Dążenie do pokrycia 100% kodu mija się z celem i jest po prostu stratą czasu.
O powodach pisania testów pisałem na początku tego cyklu. Są jednak miejsca, w których koszt napisania testu jest bardzo duży, a jego wartość – znikoma.
Zacznijmy więc od odpowiedzi na trudniejsze pytanie.
Czego nie testować?
Unikałbym testowania wszelkiej maści wygenerowanego kodu. Nie mówię tylko o plikach *.designer.cs – to już ekstremum i chyba nikt się na to nie porywa. Pod pojęciem “wygenerowany kod” rozumiem także proste gettery/settery czy konstruktory. Takich konstrukcji nie powinno się pisać ręcznie – straszna strata czasu. A tym bardziej ich testować – strata jeszcze większa. “Biznesowy” zysk z potencjalnych testów tak banalnego kodu jest praktycznie zerowy. Z pomocą przychodzą narzędzia tak proste jak snippety w Visual Studio czy bardziej skomplikowane, jak szablony z Resharpera. Z nimi wprost nie sposób popełnić durny błąd wykrywalny przez proste testy… które swoją drogą mogą taki durny błąd powielić.
Wyjątkiem od “nietestowania wygenerowanego kodu” byłby oczywiście kod generowany przez nas, ale wtedy podpada to pod testy generatora a nie kodu wynikowego.
Żmudne, nudne, męczące i baaardzo kosztowne w utrzymaniu jest pisanie testów do klas mających wiele zależności i zajmujących się głównie przekazywaniem obiektów między nimi. Z jednej strony – powinniśmy po pierwsze unikać TWORZENIA takich klas… ale z drugiej strony – wiadomo jak to w życiu jest. Zwykle miejsca takie pełnią rolę koordynatorów przepływu logiki w naszej aplikacji, same w sobie logiki takowej nie zawierając. Testy sprowadzają się wtedy właściwie do stworzenia kilku(nastu?) mocków, podania ich jako zależności, a następnie skonfigurowania metod na tych mockach. Metoda na jednym mocku zwraca jakiś obiekt, a X testów sprawdza, czy wartość ta została przekazana drugiej metody, w innym mocku. I tak w kółko. Prowadzi to do takiej sytuacji, że klasa zawierająca testy będzie miejscem bardzo często odwiedzanym podczas refaktoringu kodu, dodania nowej zależności, przedstawienia dodatkowego interfejsu czy zmiany przepływu obiektów, nawet niemającego wpływu na “core” naszej logiki biznesowej. Kiedyś stworzyłem kilkaset podobnych testów, a potem utrzymywałem je przez prawie dwa lata. Wniosek mam prosty: testy często się “psuły”, a mimo to nie pomogły w wykryciu ani jednego (sic!) błędu. Trzeba było je aktualizować, a design aplikacji w żaden sposób z tego nie skorzystał. Mało tego – ich czytanie wcale nie mówiło więcej, niż po prostu zajrzenie w kod testowanych mechanizmów – ba, było nawet bardziej męczące.
O moim konkretnym scenariuszu rozpisywać się tutaj nie będę, ale przychodzi mi do głowy całkiem chyba niezła analogia. Wszyscy znamy i lubimy wzorzec MVC, ale czyha w nim jedna bardzo niebezpieczna, i często ignorowana (przyznaję: także przeze mnie) pułapka: pakowanie logiki biznesowej do kontrolerów. Na samym początku może wydawać się to normalną praktyką: kontroler przyjmuje dane, ewentualnie poddaje jakiejś bardziej skomplikowanej walidacji, grzebie sobie w bazie, wykonuje obliczenia, wywołuje metody, znowu grzebie w bazie, a na koniec generuje widok. Prędzej czy później (niestety raczej później niż prędzej, co czyni ją bardziej niebezpieczną niż może się wydawać) takie coś wróci do nas w postaci spaghetti-code i mega-wielkich klas przepakowanych wszelkimi odpowiedzialnościami, mającymi ze sobą wspólnego tylko tyle, że użytkownik widzi je pod podobnym adresem URL. Kontroler powinien, jak sama nazwa wskazuje, kontrolować/nadzorować wykonanie logiki biznesowej, ale nie mieć o niej wielkiego pojęcia. Od logiki jest… tak, Model! W przeciwnym wypadku stosujemy anti-pattern zwany “fat controller”.
I po tym przydługim wywodzie stwierdzenie, które może być uznane za dość kontrowersyjne: poprawnie napisane kontrolery są właśnie koordynatorami nienadającymi się do testowania. Zawierają minimalną ilość banalnego kodu. Konieczność napisania testu dla kontrolera traktuję jako znak ostrzegawczy, że coś jest z kontrolerem nie tak – robi więcej niż proste przekazanie sterowania do modelu/domeny i wygenerowanie widoku.
Bajdełej, podobnie rzecz się ma z klasami “code-behind” znanymi z WebForms (albo analogicznymi rozwiązaniami, jak chociażby kodem “pod designerem” w Workflow Foundation). Można je przetestować, ale ich zawartość powinna być maksymalnie uproszczona i momentalnie przekazująca sterowanie “gdzieś dalej” (w przypadku WebForms na przykład do presenterów z wzorca MVP). Czasami co prawda sam framework wymusza na nas konieczność wydziergania niemałej liczby linii kodu w celu pokazania/ukrycia odpowiednich elementów czy zbindowania danych, ale programista na pewno znajdzie bardziej pożyteczne zajęcie niż pisanie do tego testów.
A skoro już wspomniałem o widokach…W internecie niejednokrotnie natknąłem się na próby testowania kodu zawartego w plikach *.cshtml. Pewnie w jakiś magiczny sposób można by robić to samo z *.aspx/*.ascx. Moim skromnym zdaniem mija się to z celem. Rola kodu widoku jest prosta: pokazać dane. I nawet jeśli mamy tam jakiegoś ifa to nie jest rolą testów jednostkowych sprawdzanie działania tych instrukcji. Takie coś można badać testami symulującymi przeglądarkę, ale powody inwestowania w nie czasu są trochę inne. To jednak temat na osobną notkę.
Nie powinniśmy się też skupiać na szczegółach implementacji testowanej logiki. Mamy zbadać i udowodnić “co”, a nie “jak”. Dostarczamy input, odbieramy output i tworzymy asercje sprawdzające czy wynik zgodny jest z oczekiwaniami. Dlatego też jestem przeciwny stosowaniem tzw. “strict mock” – czyli kontrolowaniu na poziomie testu kolejności wywołania metod na jakiejś zależności, badaniu ile razy dana metoda została wywołana czy upewnianiu się, że wywołano tylko metody przez nas przewidziane, a nie jeszcze jakieś inne.
Co testować?
Mówiąc prosto: wszystko pozostałe. Jakakolwiek logika biznesowa powinna mieć dokumentację w postaci testów. Idealnie, jeśli testy powstają przed implementacją owej logiki, ale nie o TDD jest ten post.
Staram się jednak podchodzić do tego zdroworozsądkowo – jeżeli pisany kod jest prawdziwie banalny to raczej nie zainwestuję czasu w pokrycie go testami. Jeśli jednak nawet w krótkiej metodzie pojawia się chociażby IF – to sytuacja ulega zmianie i wtedy kilka testów staram się rzucić. Nawet nie po to, żeby sprawdzić poprawność kodu. Raczej po to, żeby zostawić na przyszłość ślad mówiący “tak, to działanie jest zamierzone, świadomie zaimplementowałem to w taki a nie inny sposób, a testy o odpowiednich nazwach są tego dowodem“.
Testuję też wyrzucanie wyjątków przez moje metody – i testy uwypuklają scenariusze, w których taka sytuacja zachodzi. Każda świadoma instrukcja “throw” znajduje odzwierciedlenie w teście – oznacza to, że takie zachowanie jest poprawne i pożądane.
Idealnym obiektem testów jednostkowych są wszelakie algorytmy – kawałki kodu robiące coś z dostarczonymi danymi i zwracające wynik operacji na nich, bez udziału zewnętrznych zależności. To chyba wymarzone środowisko dla unit testów.
Warto również pamiętać, żeby w testach przewidzieć nie tylko sytuacje badające “czy kod robi co powinien”, ale także “czy kod nie robi tego czego nie powinien”. Pierwszy z brzegu przykład: pisząc test dla metody zwracającej artykuły opublikowane przez danego użytkownika przydałoby się również napisać drugi test sprawdzający, czy przypadkiem dodatkowo metoda nie zwraca artykułów opublikowanych przez innych użytkowników. Albo jeśli jakaś reguła biznesowa ma dać rabat uprzywilejowanemu klientowi to miło byłoby widzieć w testach weryfikację, że “normalny” klient przekazany do danej reguły owego rabatu nie otrzyma.
Nie ukrywam, że zdecydowanie ważniejszą częścią niniejszego posta są akapity traktujące o nietestowaniu. Z mojego doświadczenia wynika, że mądre inwestowanie czasu spędzonego na pisaniu i utrzymywaniu testów jest jedną z najtrudniejszych decyzji podejmowanych podczas tworzenia systemu. Posiadanie masy zbędnych, niespełniających swej roli testów może być bardziej kosztowne i spowalniające niż ich całkowity brak.
Jakie macie przemyślenia na ten temat? Czy dążenie do pokrycia 100% kodu testami to Waszym zdaniem dobry pomysł? Jakie testy można wyeliminować, a jakie są ważne? Może ktoś nie zgadza się z tym co napisałem? Chętnie poznam alternatywne opinie (poparte oczywiście stosownymi argumentami:) ).
Zgadzam się i podobnie się stosujemy. Nie zawsze co prawda testujemy wszystkie throw’y zawarte w metodzie (bo tych zazwyczaj staramy się mieć jak najwięcej) i nie używamy raczej TDD. Chociaż moje ostatnie próby potwierdzają przydatność TDD w projektowaniu. Czasami testujemy controllery na zasadzie takiej, że często mapujemy w nich dane. I to mapowanie sprawdzamy w testach (które często modyfikujemy), ale zapewniamy sobie tym sposobem to że mapowanie jest poprawne (mapowanie zawiera if’y).
Do tego posiadamy sporo testów sprawdzających czy pewne reguły przez nas zdefiniowane są realizowane (np. czy historyczne klasy zawierają wszystkie propertasy z klas z domain) oraz czy nie używamy nadmiernie czegoś (np. klasy z domain mają nie zawierać żadnych atrybutów).
pawelek,
O poruszonych przez Ciebie testach konwencji (jakieś klasy mają mieć jakieś właściwości, inne nie mają mieć atrybutów itd) mam zaplanowaną osobną notkę, jest to temat ciekawy sam w sobie:)
Zazwyczaj 70-80% pokrycia kodu logiki biznesowej / logiki aplikacji uważam za wystarczające.
"Warto również pamiętać, żeby w testach przewidzieć nie tylko sytuacje badające "czy kod robi co powinien", ale także "czy kod nie robi tego czego nie powinien"." – to właśnie można sprawdzić za pomocą mock objects. Testy behawioralne stosuję równolegle z asercjami, z tym że trend zmienia się na niekorzyść asercji. Weryfikacja stanu i odwołań do mock’a jest zazwyczaj szybsza i bardziej naturalna – przynajmniej dla mnie.
Co do pokrycia kodu testami – używacie PEXa do wygenerowania szablonu testu?
pomyk,
Nie używam PEXa, ale w następnym odcinku pojawi się do niego link w odpowiednim kontekście.
http://cleancoder.posterous.com/100-test-coverage
http://cleancoder.posterous.com/100-code-coverage-again-3984
dee,
Thx, ciekawe. Uncle Bob mówi że 100% code coverage to "obvious goal" – okej. Ja wyraziłem swoją opinię na temat jak do tego celu dążyć, tzn które testy odpuścić jeśli nie mamy nieskończonego czasu na napisanie projektu. A nie mamy. "Durne" testy zawsze można dopisać później, jeśli to że nie mamy 100% nie będzie nam dawało spać.
Zwróć też uwagę, że nie namawiam do nietestowania "niektórych ścieżek wykonania", a raczej do pominięcia testów kodu banalnego, którego testy często byłyby po prostu napisaniem tego samego kodu 2x.
100% coverage – ale w jakim kontekście? Jaki sens ma testowanie elementów UI czy elementów kodu, które wymagałyby napisania testu integracyjnego (dostęp do zdalnych zasobów, systemu plików itd.) ?
pomyk: logika biznesowa powinna mieć 100% pokrycia kodu bez dwóch zdań. Każde założenie biznesowe powinno się znaleźć w kodzie i testach. Wówczas po pierwsze chronimy się przed błędami a po drugie chronimy się (jako programiści) przed sytuacjami, że wdrożeniowcy/management/etc próbują wmówić, że coś miało być lub że było ustalone że miało działać inaczej.
Co do logiki aplikacji to tutaj już 100% pokrycia jest imho dobrą praktyką ale już nie tak wymagane jak w BI. Inna sprawa, że zamknięcie logiki aplikacji w testowalne struktury pozwoli nam na automatyzację testów aplikacji oraz odseparowanie się od UI ale to już temat na inną bajkę :)
procent: co do grubego kontrolera polecam te filmiki http://www.sitecrafting.com/blog/model-view-1/ wszystkim, którzy nie pamiętają gdzie wrzucać logikę :)
Prywatne metody – testować czy nie testować?
Samemu nie testuje bo mało eleganckie i w końcu i tak testując publiczną metodę, przetestuje również prywatną…
"Dlatego też jestem przeciwny stosowaniem tzw. "strict mock" – czyli kontrolowaniu na poziomie testu kolejności wywołania metod na jakiejś zależności, badaniu ile razy dana metoda została wywołana czy upewnianiu się, że wywołano tylko metody przez nas przewidziane, a nie jeszcze jakieś inne"
Czy na prawdę nigdy to nie ma sensu?Moim zdaniem ma to sens, jeśli testujemy faktycznie zachowanie a nie szczegóły implementacyjne (typu wywołanie metody pomocniczej, która mogłaby być zaimplementowana bezpośrednio w metodzie).
Piotr Zielinski,
Prywatne metody – nie. Jeśli jest tam coś wartego oosbnego przetestowania to nie powinno być prywatne. W większości przypadków takie prywatne metody to "helpery" zaśmiecające odpowiedzialności klasy i powinny być przeniesione do osobnego bytu, osobno testowanego, przyjmowanego przez ową klasę jako zależność.
Co do strict mock… tak jak zaznaczam w każdym poście, cały ten cykl to nie jest jakieś wademekum którego trzeba się trzymać "bo inaczej jest źle". Spisuję wyłącznie własne doświadczenia, niezbyt posiłkując się nawet jakimikolwiek książkami czy teorią. I ja takich testów nie stosuję PRAWIE nigdy.
Test testujący wywołanie bądź niewywołanie metody napisałem świadomie RAZ – był to test identyfikujący bardzo trudny bug, występujący w środowisku mocno wielowątkowym, wyłącznie przy dużym obciążeniu systemu.
Staram się, aby moje testy nie sprawdzały czy jakakolwiek metoda na mocku była wywołana. One mają stwierdzić "jeśli zewnętrzny system, zasymulowany mockiem, zwraca X, to testowana metoda ma zwrócić Y". Nigdzie nie ma powiedziane "testowana metoda ma wywołać metodą Z na mocku", chociaż w domyśle (ale tylko tam) oczywiście o to chodzi.
Piotrze: ja osobiście nie testuje metod prywatnych. Tak jak piszesz, testy metod publicznych i tak przetestują metody prywatne, a jeśli nie przetestują to pewnie nie jest ta metoda w ogóle potrzebna i można ją usunąć :) (przy podejściu tdd i 100%cc oczywiście ). IMHO błędem jest pisanie testów dla każdej linijki. Zgadzam się z tym co napisałeś, testujemy zachowanie a nie implementację. Jeśli metoda ma zwrócić 4 element kolekcji to to testuję i to mnie interesuje (+ wariancje w stylu co jeśli kolekjca ma 4 elementy, 3, 0 etc.) a szcególy implementacji w tej sytuacji to sprawa drugorzędna.
rek: weryfikacja, czy produkt działa tak a nie inaczej leży w gestii testów integracyjnych. Natomiast test jednostkowy powinien spełniać 2 cechy:
1. Wykonywać się szybko
2. Pomagać w lokalizacji bugów
Cytując R. Martina: "testy jednostkowe, które wykonują się długo, kończą jako nie wykonywane lub wykonywane bardzo rzadko". Nie piszemy testów, które nie spełniają swojego zadania. Testy jednostkowe mają poprawiać efektywność pracy programisty a nie pełnić roli kotwicy która opóźnia pracę nad projektem.
Czytajac niektore komentarze pod tym postem mozna nabrac zlych praktyk….. Procencik, przeczytaj jeszcae raz co napisales o metodach prywatnych… Najpierw nie chcesz testowac a potem chcesz przenosic do "osobnego bytu" i testowac tam… Trzymaj sie jednej wersji, bo inaczje copy-pasterzy pogubia sie i beda zwalac na Ciebie! :) A copy-paster tudzież code-monkey NIE MYSLI, tylko najczesciej uprawia spychologie :) Wies, be aware.
Po za tym drobnym niuansem z metodami prywatnymi, podoba mi sie Twoja ewangelizacja polskiej C#-sceny.
pomyk: może zrobiłem pewien skrót myślowy. W żadnym wypadku nie chodziło mi o zastąpienie testów integracyjnych testami jednostkowymi. Testy jednostkowe imho mają testowaĆ zachowanie klasy/metody – sprawdzenie czy spełnia założenia programisty i to miałem na myśli. Zachowanie całej aplikacji to już coś innego (tutaj przydatny może być choćby Martinowy FitNesse)
@rek:
obejrzałem wczoraj te filmiki, świetna sprawa, dzięki za linki:)
Pawel Klimczyk,
Że się podoba – to dobrze:).
Co do metod prywatnych: niejasno się chyba wyraziłem. Nie chcę testować metod prywatnych, bo jak wcześniej koledzy napisali, zostaną i tak przetestowane niejawnie (a jak nie to tez nic się nie stanie bo są zbędne). ALE metoda prywatna metodzie prywatnej nierówna. Niektóre WYMAGAJĄ testowania. Przykładem może być jakaś pomocnicza, skomplikowana ekstrakcja ID obiektu ze stringa zawierającego także inne informacje. Ktoś zrobi z tego pomocnicza metodę prywatną i głowi się jak to przetestować, podczas gdy logika ta powinna być zawarta w ogóle w innym miejscu – i tam przetestowana (np w klasie nazwanej – jakże by inaczej – IdExtractor:) ).
procent, Pawel Klimczyk: w sumie można chyba by uogólnić to tak: Metod prywatnych nie testujemy, jeśli uważasz, że metoda prywatna powinna być otestowana to znaczy że zrobiłeś coś źle…. refactor it….. wyciągnij do osobnej klasy (SRP) wraz z testami. Co Wy na to ?
@rek nailed it:)
rek, procent. Dokladnie! Masz cos prywatnego, co musisz zewnetrznie przetestowac -> zabrnales w guwno (moze nawet po pas) -> refaktoryzuj ASAP. Sam proces refaktoryzacji powinien byc codziennym zjawiskiem u dobrego programisty. Ciagle doskonalenie.
Sam proces refaktoryzacji powinien być wkomponowany w czas programowania i niewidoczny. Ja nie zauważam że coś refaktoruję, po prostu widzę, że trzeba coś poprawić aby napisać coś nowego. W ten sposób w sumie w gówno nie wpadam. A testuję najmniej jak się da :) Czyli właściwie tylko Biznesowe warunki (modyfikację klas domenowych), od czasu do czasu jakieś pierdoły, no i mamy testy konwencji o których dowiedziałem się stąd, że są testami konwencji.
mam wrażenie, że operacje I/O zostały tutaj pominięte, nie raz spotkałem się, z opinią, że testy z użyciem bazy danych nie mają racji bytu, czy masz natomiast jakąś strategie do testowania mapowań w orm-ach, a w zasadzie będąc w temacie co testować – czy w ogóle to testujesz?
@wiero
w NH masz prosty sposob to przetestowanie czy mapowanie dziala czy tez nie. Do tego jak korzystasz z Fluent to tez masz sposob na sprawdzenie czy mapowanie dziala.
wiec za duzo to tutaj strategii nie ma.
zas co do innych orm’ow, a sa jakies (rozsadne) ? ;)
pawelek,
Nie wiem czy "testy konwencji" to jakaś oficjalna nazwa:). Z tym terminem spotkałem się na blogu Krzysztofa Koźmica ( http://kozmic.pl/2011/03/09/testing-conventions/ ) i wg mnie bardzo pasuje.
wiero,
Testowanie systemu plików – to zależy. Jeśli jakaś klasa potrzebuje odczytać/zapisać plik to zwykle robię prosty wrapper na System.IO.File z interfejsem ReadFile() i WriteFile() i wstrzykuję to normalnie jako zależność, mockując w testach.
A testy bazy danych… tak, moim zdaniem lepiej testować bazę niż mockować dostęp do danych. W przypadku NHibernate jest to niezwykle proste dzięki możliwości zastosowania SQLite (więcej na ten temat pisałem tutaj: http://www.maciejaniserowicz.com/post/2009/11/20/NHibernateStarter-zaczatek-aplikacji-z-NHibernate-NHibernateLinq-Fluent-NHibernate-nUnit-i-SQLite.aspx ).
Witam,
Po pierwsze świetny post (jak i poprzednie z tej kategorii). Chciałbym podzielić się swoimi przemyśleniami, jako że ja bardzo interesuję się tym tematem.
Po pierwsze główne pytanie: czy starać się osiągnąć 100% code cover? Moim zdaniem (choć peważnie zgadzam się z wójkiem Bobem) nie. To jest przesada. Widoki zdecydowanie nie wymagają testowania (zakładam, że są napisane tak jak powinny – bez logiki). ViewModele również odpadają. Gettery, settery, konstruktory (bez logiki) również odpadają. Co do kontrolerów, to się zgodzę, że jeśli będą miały odpowiednią strukturę (pobierz dane, zmapuj na viewModel, wrzuć do widoku), nie wymagają testowania. Walidację przerzuciłbym na atrybuty i je testował!
Co do logiki która pojawia się w kontrolerach, to uważam, że warto stworzyć specjalną warstwę servise’ową do różnych manipulacji na danych i je testować. Samo mapowanie (ja używam automappera) również testuję (automapper ułatwia to bardzo). Logika biznesowa chyba nie wymaga komentarza (każdy pisał, że to obowiązek i ja też tak uważam).
Metody prywatne, które nie są efektem refactoru (czyli kiedyś nie były częścią metody publicznej) nie mają prawa bytu. SRP! Osobna klasa (helper) wstrzykiwana jako zasób i (dobrze) wytestowana.
Operacje na plikach ? Tfu, operujmy na strumieniach. Można na nich asertować i wszystko czego dusza zapragnie. A jedyne miejsce, gdzie fizycznie następuje dostęp do pliku owrapować (jak napisałeś w komentarzu) i stubować.
Paweł Olesiejuk: proponuję obejrzenie filmów podlinkowanych przez dee. Uncle Bob mówi i zgadzam się z nim w 100% w tej kwestii, że jedyny cel to 100%. Jest to cel w większości nieosiągalny co nie zmienia faktu, że jest to jedyny słuszny i akceptowalny cel – 100%.
Podejście w stylu nie testuję geterów, seterów konstruktorów etc powoduje, że otwiera się luka. Przecież ta metoda jest trywialna – też jej nie przetestuje itd. W efekcie możemy mieć coraz mniej testów a przecież nie o to chodzi. Gettery settery i konstruktory i tak zostaną przetestowane przez inne metody więc nie ma sensu pisać bezpośrednich testów na te elementy (chociaż czasem, w bardzo specyficznych warunkach, warto) a jeśli np. getter nie został przetestowany to najwyraźniej nie jest używany więc może można go usunąc.
To samo tyczy się metod prywatnych (dyskusja w innej części cyklu). Nie testujemy ich bezpośrednio a jeśli testy publicznego api klasy nie przetestowało metod prywatnych to należy je usunąc.
@rek,
Filmy oczywiście widziałem. Zgadzam się w 100%, że getery, czy konstruktory zostaną przetestowane pośrednio. Ale jeśli mówimy np. o klasach POCO, np. ViewModel, to dla mnie logicznym jest, że nie będę pisał dla nich testów, bo nawet jeśli, to jakby miały wyglądać? Mówiąc o konstruktorach, oczywiście prawdą jest, że pośrednio też je testujemy, ale załóżmy, że klasa ma 5 różnych konstruktorów (zakładamy, że konstruktor robi tylko przypisania) to czy w testach każdy z nich powinien być użyty? (może tak, ale ja uważam, że nie jest to obligatoryjne)
imho ViewModel obowiązoko powinien być przetestowany. Jeśli mówimy o tym samym, to ViewModel agreguje różne obiekty z modelu biznesowego. Testując VM i jego comendy doprowadzamy do sytuacji, gdzie 100% aplikacji możemy przetestować za pomocą testów jedn. i wtedy możemy całość zautomatyzowac, każdy scenariusz, każdy user story czy historię czy z czego kolwiek i nnego korzystamy, możemy zasymuloawć w testach automatyczynych i to jest piękne. Oszczędność czasu i pieniędzy. W takiej sytuacji wrzystko co jest powyżej ViewModel czyli UI pozostawiamy grafikowi :)
a klasy POCO się same przetestują :)
Chyba inaczej rozumiemy (implementujemy) koncepcję ViewModeli. Dla mnie ViewModel, to "martwy" obiekt (POCO) bez logiki. I ostanio nawet nie używam agregacji obiektów biznesowych (a jeśli nawet, to po co testować drugi raz obiekty biznesowe?), tylko "spłaszczam je" (wyciągam tylko propercje potrzebne na konkretnej stronie. Z tego punktu widzenia uważam, że ViewModel nie wymaga testowania.
Ogólnie uważam, że temat został wyczerpany, dalsza dyskusje jest jałowa, bo doszliśmy do pewnego konsensusu, a drobne różnice wynikają raczej z różnic w projektach niż ogólnej koncepcji. Mimo wszystko uważam, że była(jest) to bardzo pouczająca dyskusja. Dzięki.
Pozdrawiam.