Okres wakacyjno – urlopowy czas zakończyć i wrócić do regularnego blogowania 🙂

Test jednostkowy powinien trwać milisekundy, a tu mija pierwsza, piąta, dziesiąta sekunda, coś jest nie tak. Zaglądamy do kodu czyżby nasz test jednostkowy był uzależniony od kosztownej zewnętrznej zależności. Pudło, wszystkie rzeczywiste zależności na potrzeby testów jednostkowych zastały zastąpione przez atrapę (mock). W ramach wpisu nie rozróżniam atrap na Fake, Stub, Mock, Dummy, Spy, wszystkie obiekty traktuję jako Mock. W celu zrozumienia problemu przejdźmy do poniższego przykładu.

Analizowany przypadek

Przypadek testowy postanowiłem uprościć do minimum, by uniknąć wchodzenia w bardziej złożoną logikę biznesową. Mamy w klasie Calc metodę wykonującą obliczenia na obszarze 1000×1000 metrów z krokiem co 1 metr, czyli 1000×1000 elementów. W ramach obliczeń dla lokalizacji x, y będzie odczytywana wartość wysokości terenu z zewnętrznego serwisu, która wymagana jest do dalszych obliczeń (w naszym przypadku pomnożymy ją przez stałą wartość). W ramach testów jednostkowych zewnętrzną zależność zostanie zasymulowana makietą.

W ramach testów zewnętrzna zależność IMapService została zasymulowana przez atrapę z frameworka Moq, NSubstitute i ręczną implementacją sztucznego obiektu. Czasy wyświetlane przez runnera testów nie są dość dokładne, tym samym do testów wydajności, w celu zmierzenia czasu wykonania została wykorzystana biblioteka BenchmarkDotNet. Część wspólną kodu, która nie wpływała na różnicę czasowe analizowanych trzech przypadków, czyli blok Assert i deklaracja zmiennej z oczekiwanym wynikiem została przeze mnie pominięta (usunięta). Poniżej znajdują się kody trzech metod, które zostały przeanalizowane pod kątem czasu wykonania.

Wyniki Benchmarku

Mock benchmarkW analizowanym przypadku wykorzystanie bibliotek do mockowania jest nie korzystne wydajnościowo. Test bez frameworka wykonuje się ok. 2083 razy szybciej niż przy użyciu frameworka NSubstitute. Dla Moq uzyskano trochę lepszy czas, ale i tak w porównaniu do metody CalcWithoutFramework wypada słabo.

Podsumowanie

Co z tym można zrobić? Po pierwsze zajrzeć do kodu i przeanalizować czy logika biznesowa została prawidłowo zaimplementowana. Po drugie warto pamiętać ze istnieje strata wydajności, gdyż te frameworki nie są przeznaczone do pracy z dużymi danymi, oraz dużą ilością wywołań mockowanych metod w ramach testu jednostkowego. Frameworki mają nam pomagać, ale pamiętajmy żeby je stosować z głową.