fbpx
devstyle.pl - Blog dla każdego programisty
devstyle.pl - Blog dla każdego programisty
2 minut

DevTalk#12 – O mockach z Pawłem Klimczykiem


16.03.2015

pawel-klimczykW dwunastym już odcinku (czas leci!) mam przyjemność ponownie podywagować na temat bardzo mi bliski: testy. A konkretnie: testowanie w kontekście wykorzystania “isolating frameworks”, czyli po ludzku: mocków. Partnerem w rozmowie jest Paweł Klimczyk – programista, prelegent i “szef dotnetów na fejsie” :), czyli grup .NET Developers Poland oraz .NET Developers Poland Job Market. Na Twitterze: @pwlklm.

Podczas audycji możecie posłuchać o tym co to są mocki i na jakie grupy się je dzieli (i czy ma to sens). Jakie frameworki w świecie .NET pozwalają na wykorzystanie mocków (i jak je można skategoryzować). Do tego wpada kilka pobocznych wątków, jak na przykład: jak testować metody prywatne?

Konkurs: w tym odcinku mam dla Was egzemplarz książki “The Art of Unit Testing” Roya Osherove. Jak zwykle (ale monotonnie, co? ;) ) powędruje on do jednej z osób, które wezmą udział w dyskusji która powinna wywiązać się pod tym postem. Piszcie zarówno na temat merytoryki odcinka, jak też ogólnie o DevTalku.


Montaż odcinka: Krzysztof Śmigiel.
Ważne adresy:

Linki


Muzyka wykorzystana w intro:
“Misuse” Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 3.0
http://creativecommons.org/licenses/by/3.0/
0 0 votes
Article Rating
27 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Piotr
Piotr
9 years ago

Do mockowania System.DateTime.Now używam takiego rozwiązania:
public static class SystemTime
{
public static Func Now = () => DateTime.Now;
}

Użycie:
DateTime now = SystemTime.Now();

A w testach:
[SetUp]
public void Initialize()
{
SystemTime.Now = () => new DateTime(2018, 01, 01);
}

Piotrek
Piotrek
9 years ago

Apropo auto-mocking containers, to od jakiś dwóch miesięcy używam AutoFixture (https://github.com/AutoFixture/AutoFixture) i przyznam szczerze że świetna sprawa. O ile w nowych aplikacjach (aplikacjach pisanych od zera czy no nazwijmy je bardziej na czasie) duża ilośc zależności klasy rzeczywiście świadczy o bad design,o tyle przy aplikacjach ponad 10-letnich i tzw. legacy code, gdzie klasa przyjmuje 5 zależności i lepiej nie wyobrażam sobie mockowania wszystkich zależności. Zwłaszcza gdy potrzeba przetestować (co głownie jest ideą testowania) jakiś kawałek, w którym user znalazł błąd, a refactoring nie wchodzi w rachubę ;) Używając automocking containers tworzysz sobie sut’a, mockujesz co potrza i jazda.

Odnośnie dzisiejszego odcinka: poziom dobry, rozmowa na temat. Okazało sie że całe życie używałem constrained framework mocks, a tu jest jeszcze nowy level do wbicia w postaci uncinstrainded mocking frameworks ;)

Grzegorz
Grzegorz
9 years ago

Hej!
Bartdzo ciekawa seria podcastow – dzis przesluchalem 75% z calej serii.
Co do mockowania – zawsze mnie intruguje jak dobrze mockowac ef – bo setup czasami zajmuje caly ekran, a mnie uczono, ze jak cos jest na caly ekran – to znaczy ze jest za duze (a wtedy nie bylo rozdzielczosci fullHD).

Mysle, ze przeslucham podcast dwa, trzy razy i wroce z pytaniami, bo musze zaladowac nowe namesaces do osrodka decyzyjnego…
G

Ireneusz Patalas
Ireneusz Patalas
9 years ago

@Grzegorz: co do Entity Frameworka to niedawno znalazłem coś takiego jak Effort: http://effort.codeplex.com/ Wygląda całkiem zacnie, ale przyznam, że jeszcze nie sprawdzałęm, bo teraz jestem w innym projekcie i EF nie mamy.

Co do fake’owania DateTime.Now to jest jeszcze jeden fajny sposób, który mi się bardzo podoba, bo nie wymaga żadnych zmian w kodzie (no prawie… ale o tym zaraz). Fody.Ionad (https://github.com/Fody/Ionad). Dla tych co nie znają Fody to w dużym uproszczeniu można za jego pomocą (a dokładniej jego pluginów) dokonywać “post-processingu” kodu tuż przed jego kompilacją. Nie ma to wpływu na oryginalne pliki z kodem, ponieważ dzieje się to w locie.
Fody.Ionad daje możliwośc podmiany dowolnego statycznego odwołania na inne. Przykład z DateTime.Now jest akurat na stronie, więc nie będę go bezsensownie przeklejał. Jest to o tyle fajne, że możemy to zrobić praktycznie w dowolnym momencie istnienia projektu nawet mając już bardzo dużo odwołań do DateTime.Now. Nie trzeba wprowadzać produkcyjnie tej “furtki dla testów” oraz nie trzeba w ogóle modyfikować kodu. Zmodyfikuje się on oczywiście w locie przed buildem, ale oryginalny kod nie zostanie zmieniony i w kontroli wersji cały czas mamy stary dobry DateTime.Now.
Jak ktoś zapomniał o wprowadzeniu warstwy abstrakcji dla czasu (IMHO to już zakrawa lekko na paranoję, bo ta warstwa izolacji pełni tylko rolę furtki dla testów, nie ma sensu wstrzykiwać tutaj np. za pomocą DI innej funkcjonalności) to może to być ciekawa alternatywa dla TypeMock/Microsoft Fakes, które dla mnie są raczej niewymagającym refleksji workaround’em. Dodatkowym atutem będzie fakt, że jeśli przy projekcie pracuje więcej osób to nie muszą one uczyć się nowego dostępu do czasu (przez jakieś własne SystemTime.Now czy podobne), a co za tym idzie nie ma ryzyka przegapienia kilku wystąpień.

@procent: to już któryś raz jak widzę, że używasz terminu Microsoft Moles. Ten twór MS’a przekształcił się w Microsoft Fakes w okolicach 2012 roku i jako Moles nie jest już rozwijany. MS Fakes natomiast jest już integralną częścią VS (na pewno 2013, nie wiem czy 2012 też). Z własnych doświadczeń nie polecam go jednak… pomijając kwestie ideologiczne wspomniane wyżej, mieliśmy z nim różne dziwne problemy. Niektóre objawiały się dopiero na build serwerze a lokalnie działały. Bywają problemy z odświeżaniem wygenerowanych bibliotek z fake’ami (np. zmieniła się wersja), poza tym zajmuje to dużo miejsca. Tak, wiem, miejsce teraz jest tanie, ale nie lubie patrzeć jak projekt zajmuje przykładowo 1GB, a z tego 800MB to wygenerowane fake’i przy różnych projektach w solucji. Można to mocno poprawić generując fake’i tylko dla wybranych typów, ale to niewygodne i powoduje kolejne problemy przy odświeżaniu. Jestem przeciwnikiem używania tego typu narzędzi, ale muszę przyznać, że jest to często dużo szybsze rozwiązanie niż refactor architektury, która nie zawsze jest idealna.

Piotrek
Piotrek
9 years ago

@Maciek
Co rozumiesz pod pojęciem “… nadużywanie automocking containers…”? W tej chwili widzę więcej zalet stosowania ich niż wad, no ale może są jakieś wypowiedzi mądrych głów, które chętnie przeczytam, żeby zgłębić temat… bo może się okazać że mam oczy szeroko zamknięte w temacie ;)

Ireneusz Patalas
Ireneusz Patalas
9 years ago

@procent: Zgadza się, że code weaving to nie jest dobra metodologia do pisania aplikacji, ale… to jest tylko narzędzie i podobnie jak nóż może zostać użyte dobrze lub źle :)
Ja Fody znam głównie z pluginu do INotifyPropertyChanged, o którym wspominaliście z Basią przy okazji AOP i tam nadaje się to wg mnie świetnie. Oczywiście zaciemnia częściowo kod, bo to dzieje się jakby poza kontrolą, ale w tym przypadku chyba jednak wole nauczyć wszystkich jak to działa niż mieć klasy modelów z ręcznie zaimplementowanym tym interfejsem dla każdego propsa rozdmuchane na 3 kilometry.
W przypadku DateTime.Now jestem nawet w stanie wybaczyć użycie Isolatora czy MS Fakes, mimo tego że za nimi nie przepadam. Gorzej kiedy ktoś zaczyna tym mockować własny kod (no nie zawsze dosłownie własny, ale kod projektu, a nie .NET’a). Wtedy to sygnał, że architektura kuleje i przydałby się refactor. Tutaj niestety jeszcze do głosu często dochodzi biznes, który może dać veto dla większego refactora z różnych powodów. Świat nie zawsze jest taki czarno-biały i nawet jak dev chce dobrze to może nie mieć takiej możliwości.

Piotr Perak
9 years ago

Hej! Testy to temat mi również bardzo bliski.

Co do różnic pomiędzy stub-ami i mock-ami to trochę zagmatwaliście :). Jest jedna, logiczna. Fizycznie niczym się od siebie nie różnią. Na stub-ach nie robimy asercji/weryfikacji. Robimy to na mockach. Stuby zapewniają niezbędne dane wejściowe, mocki pozwalają weryfikować poprawność działania kodu. Częstym błędem jest robienie asercji na obiektach, które powinny być traktowane jako stub-y w danym teście.

Co do testowania metod prywatnych to ja akurat nie widzę żadnego problemu. Tego po prostu się nie robi :) Prywatne metody to szczegół implementacyjny. Testujemy interfejs publiczny klasy. To, że metoda publiczna woła 10 metod prywatnych nie ma znaczenia. One są testowane pośrednio przez test metody publicznej. Gdybyśmy testowali metody prywatne otrzymalibyśmy testy, które wywalają się przy każdym refactoringu. Ostatnio coraz częściej słyszę głosy, że Unit testy to za niski poziom i że przeszkadzają w refactoringu. Wg mnie tylko te źle napisane.

Chyba nie zrozumiałem Waszego komentarza odnośnie tworzenia nowych klas jeżeli jest dużo metod prywatnych. Ja nie widzę niczego złego w tym. Wręcz przeciwnie! Wzorzec composed method sugeruje rozbijanie dużych metod na wiele małych co czyni kod o wiele bardziej zrozumiałym/samo dokumentującym się. Tego samego uczy Uncle Bob. Tworzenie klas tylko po to, aby powstały jakieś metody publiczne, aby można je było testować jest moim zdaniem błędem (jJuż wyżej napisałem, że bez tego można je testować pośrednio). Prowadzi do eksplozji ilości klas, które są publiczne chociaż wcale nie powinny, lub internal i zmuszanie do korzystania z InternalsVisibleTo.

Piotr Perak
9 years ago

A jeżeli w wyniku refactoringu z metod prywatnych powstaje klasa to wcale nie trzeba jej wstrzykiwać. Co więcej, wcale nie trzeba pisać dla niej nowych testów. Te testy już istnieją! To testy klasy, która zawierała te prywatne metody wyniesione do nowej klasy. Pisał o tym kiedyś na swoim blogu Uncle Bob.

Piotr Perak
9 years ago

Czy wszystko nazywamy mockami, czy część stubami, a część mockami nie ma takiego znaczenia. Ważne, abyśmy wiedzieli na czym nie powinniśmy robić asercji.

Co do SRP to nie musimy o tym pisać. Jasne, że zgodność z nią najpierw.

Jakoś nie czuję tego ze złożonością metody prywatnych. Z definicji wykonują one część pracy metod publicznych. Jeżeli złożoność metody publicznej to jej kod + kod wywoływanych metod prywatnych to siłą rzeczy metody publiczne są bardziej skomplikowane niż metody prywatne. I każda ścieżka w metodzie prywatnej jest osiągalna przez interfejs publiczny klasy. Jeżeli nie, to można taką ścieżkę usunąć, bo jest nieużywana.

U mnie metody prywatne to często 1-3 linijki powtarzającego się w klasie kodu. Nie zdarzyło mi się, aby jakoś utrudniały mi testowanie.

Grzegorz
Grzegorz
9 years ago

No no ładnie się tu porobiło :-)
@Ireneusz Patalas – dzięki za wskazówkę – bedę to maglował z szefem – szczególnie ze zapytania testuje w LinqPad’dzie i nie widze tutaj wiekszego sensu na inne testy.
@Maciej Aniserowicz – fajni by bylo uslyszec cos na temat testo UI – ja osobiscie zrobiłem piersze kroki w Selenium z driverem pod IE i firefoxa oraz w zeszlym roku zautomatyzwoalem testy UI dla aplikacji formatkowej uzywając SIKULI (no i się trochę pytona poduczyłem)
Testy UI są dla mnie o tyle ciekawe, gdyz dziś UI tak napradę decyduje o sprzedazy aplikacji i odbiorze przez klienta, a od kilku miesięcy siedzę w projecie webowym i mamy tu wiele kwatków które trzeba pięlędnowac i podlewać testami (smingus dingus idzie)

pozdrowienia!

Grzegorz
Grzegorz
9 years ago

i przepraszam za pozjadane znaki…. byłem przed przerwą śniadaniową :P

Piotr Perak
9 years ago

@Grzegorz

Jeżeli chodzi o mockowanie EF to jest to bardzo proste. Tworzysz interfejs np. IMojDbContext, który jako właściwości ma IDbSet. W testach korzystasz z InMemoryDbSet i ot cała filozofia. Tylko bądź świadom jednej rzeczy. Teraz zapytania to LinqToObjects, a więc przejdzie wszystko. Te same zapytania odpalone na bazie danych mogą być nie wspierane. Więc to kwestia dyskusyjna, czy te testy coś dają.

Bogusz
9 years ago

Updated: DevTalk – Programistyczny głos w Twoim domu
http://groopmark.com/link/details/181/devtalk-programistyczny-glos-w-twoim-domu?cid=11

Grzesiek Gałęzowski
Grzesiek Gałęzowski
9 years ago

Dzięki za dev talka!

Zanim skomentuję (i ktoś odbierze mnie jako agresora który się czepia :-)), chciałem napisać, że fajnie się słuchało, miło było poznać Wasze doświadczenia i że ten dev talk był dla mnie pożyteczny. A teraz komentarz :-)

Osobiście szkoda mi trochę, że mówiąc o mockach skupiliście się na temacie Constrained/Unconstrained (może tytuł powinien być troszkę inny?), a pominęliście podstawowe źródło wiadomości na ten temat, czyli Growing Object Oriented Software Guided By Tests. Niestety, jakoś w .NETowym środowisku ignorowanie tej książki i tego co w niej jest napisane jest zjawiskiem powszechnym. Chyba nawet sam Roy Osherove przeczytał ją dopiero po tym jak wymyślił swoją terminologię “isolating frameworks” (IMO zupełnie rozmijającą się z tym, po co mocki były pomyślane). Mocki były też dziwnie rozumiane w innych pozycjach o sporym autorytecie, np. Meszaros w swoim XUnit Test Patterns zawarł je w sekcji “Back Door Manipulation” (!!!).

Szkoda, bo fajnie by było przybliżyć słuchaczom podejście tam zawarte. Z ignorowania tej książki i tego co jest w niej napisane (autorzy zanim wprowadzają mocki, przez prawie kilkadziesiąt stron wyjaśniają swój “opinionated view” – poglądy na projektowanie obiektowe i jak mocki tam pasują) moim zdaniem wzięła się większość tzw. “argumentów przeciw mockom”, jak np. ten, że mocki psują enkapsulację, a także ten, że lepiej jest używać mocków poza modelem domeny itp. (w sumie to nie mówicie o tym, ale jakieś tam echa pod koniec dyskusji pobrzmiewają).

Jak napisał kiedyś Steve Freeman, jeden z autorów: “mocks arise naturally from doing responsibility-driven OO. All these mock/not-mock arguments are completely missing the point. If you’re not writing that kind of code, people, please don’t give me a hard time.” (https://groups.google.com/forum/#!msg/growing-object-oriented-software/rwxCURI_3kM/2UcNAlF_Jh4J)

Nat Pryce, drugi z z autorów, również bardzo mocno uzależnia “stosowalność” mocków od przyjętego stylu projektowania: “when focusing the design (and therefore tests) on messaging protocols, I do not specify anything about the state of an object. I only test in terms of incoming and outgoing messages to/from the object under test. State and mutation is implied by the test when the object’s response to later messages is affected by earlier messages it has received. But I don’t specify what that state is and how it is represented. It’s an internal implementation detail.

That’s why I argue that there are not different kinds of TDD. There are different design conventions, and you pick the testing techniques and tools most appropriate for the conventions you’re working in. And different parts of the system will probably use different conventions.” (https://groups.google.com/forum/#!topic/clean-code-discussion/A6sJQjnwvnA).

Jakiś czas temu zacząłem pisać książkę na leanpubie o TDD (nie podam linka, żeby nie było że autopromocja ;-)) i przyjąłem sobie za punkt honoru, że wyjaśnię przystępnie podejście do obiektowości które sprawia, że mocki są użyteczne. Z krótkiego wstępu zrobiło się ponad 100 stron, bo temató jest sporo: interfejsy, protokoły, klasy, kompozycyjność, kompozycjonalność, refaktoryzacja kompozycji obiektów do języków specyficznych dla domeny, obiekty wartości (Value Objects) itp.

Btw, jeśli chodzi o date/time – ja przekazuję “bardziej domenowe” opakowanie do wszystkich obiektów które tego potrzebują. Jeśli rzeczywiście jest tak, wiele miejsc potrzebuje daty i czasu, to coś w mojej architekturze prawdopodobnie jest nie tak – staram się, by data/czas były używane przez malutką liczbę obiektów, które następnie są przekazywane dalej jako potężniejsze abstrakcje. Wolę, żeby jak najwięcej obiektów zależało od tych potężniejszych abstrakcji, bo pomaga mi to optymalizować design pod kątem zastępowalności (jeśli mam interfejs np. IDevTalkTime – jak dużo sensownych różnych implementacji takiego interfejsu mógłbym wymyśleć? Dwie? Trzy? Dlatego jak dla mnie jest to dosyć słaba abstrakcja i wolę ją ukrywać jak się da).

Książka Osherove’a (mam na myśli wydanie pierwsze – drugie podobno jest sporo zmienione) jest książką dla początkujących i nawet sam autor przyznał, że wizja projektowania obiektowego tam zawarta była naiwna (nie mam pod ręką źródła, ale to była jakaś prelekcja), dlatego warto by polecić czytelnikom słuchaczom jakieś bardziej zaawansowane pozycje.

Pozdrawiam serdecznie i dzięki jeszcze raz za DevTalka!

Grzesiek Gałęzowski
Grzesiek Gałęzowski
9 years ago

Eh, wygląda na to, że zjadło mi wszystkie biało znaki :-(. Przepraszam za tę “zupę” powyżej…

Qba
Qba
9 years ago

@Maciej Aniserowicz
Co myślisz o urozmaiceniu dev talków o video w którym byłoby widać prowadzącego i gościa. Oczywiście osobno, wystarczyłoby włączyć kamerke podczas rozmów.

Paweł Klimczyk
9 years ago

@Piotr Perak:
Odnosnie roznic stub/mock – dobrze piszesz. Ja chcialem rozwinac troche wypowiedz i pokazac wiekszy kontekst. W programowaniu przewaznie uzywam mockow.
Metody prywatne wymagajace testowania – kurde, zrobilm bym z tego klase osobna, bo widocznie to co jest w srodku robi sporo i powinnno byc wyabstrahowane.

@Grzegorz:
UI Testing – temat na osobny podcast :) W tym podkascie chodzilo o pokazanie samej idei mockow.

@Grzesiek Gałęzowski:
Growing Object Oriented Software Guided By Tests – to juz temat TDD.
Ksiazke przeczytam :). Nawiazajac do Twojego podejscia do mockowania DateTime i wprowadzania abstrakcji to jest ono dobre, gdy buduje sie system. Problem pojawia sie gdy lądujesz w projekcie, gdzie kod ma juz kilka lat i dodajesz nowe funkcje. Wtedy trudno abstrahowac DateTime i (może) łatwiej mockować to co jest.

Ciesze sie, że wywiązała się tutaj taka dyskusja.

Grzesiek Gałęzowski
Grzesiek Gałęzowski
9 years ago

@Paweł Klimczyk GOOS to temat TDD, ale bardzo mocno mocków właśnie (choć nie tylko). Z wstępu do książki: “Our original motivation for writing the book was to finally explain the technique of using mock objects, which we often see misunderstood.”. Chociaż owszem, mocków można używać inaczej trochę niż jest opisane w tej książce, np. do starego kodu. Tylko wtedy nie są to takie mocki w sensie stricte, gdyż oryginalne mocki rzucały wyjątki gdy otrzymały niespodziewane wywołania (w świecie C# i Javy uważa się obecnie to podejście za przestarzałe i prowadzące do kruchych testów, natomiast JMock – biblioteka autorów GOOS – wciąż opiera się na takich właśnie mockach i autorzy twardo stoją przy stanowisku, że w takim sposobie wykorzystania mocków jaki oni preferują jest to lepsze podejście, dlatego że dla nich naturalnym podejściem do projektowania jest projektowanie “protokołocentryczne”).

Z tym Date Time w starym kodzie to zgoda, natomiast pole manewru zależy od narzędzi. Jeśli ma się dobre automatyczne narzędzie do refaktoryzacji, to najczęściej nie trzeba mockować tego co jest – w r# często sprawę załatwia kombinacja “Extract Method-> Extract Class->Introduce Parameter (jako parametr konstruktora)->Extract Interface->Use Base Type Where Possible”, którą można zrobić niemal mechanicznie.

Kurs Gita

Zaawansowany frontend

Szkolenie z Testów

Szkolenie z baz danych

Książka

Zobacz również