Kilka dni temu podczas oglądania Gry o Tron ogarnęła mnie tak przemożna chęć pokodowania czegoś ciekawego, czegoś nowego, liźnięcia tego o czym tylko słyszałem, że ledwo doczekałem do końca odcinka żeby usiąść do komputera. I tak narodził się Redirector.
Będzie to taki mój własny prosty “url shortener”, dzięki któremu chociaż trochę poznam WebAPI, RavenDB i AngularJS. Nie wiem czy skończę, nie wiem czy rozwinę, nie wiem czy będę używał. Wiem, że te parę godzin które na razie nad nim spędziłem były przyjemne i rozwjiające, a o to przecież chodzi.
Przez pół odcinka serialu rozważałem w głowie co właściwie chciałbym finalnie osiągnąć, żeby nie usiąść do Visuala i nie zacząć bezsensownie klepać byle czego – bo to raczej prędzej niż później ląduje w koszu. Po odcinku odpaliłem OneNote i tam wpisałem parę punktów… a następnie pomyślałem, że od dawna zamierzałem poruszyć na blogu temat MSpec i nigdy chyba tego jeszcze zrobiłem. Więc wraz z Redirectorem i niniejszy post na świat przychodzi.
Machine.Specifications (bo tak brzmi pełna nazwa biblioteki) to framework do tworzenia testów BDD-way, czy też “context specification”-way. Efekty, jakie można za jego pomocą osiągnąć, są takie same jak przy użyciu xUnit, NUnit czy MSTest, ale sposób pisania testów jest nieco inny. Swego czasu, dobre dwa lata temu, napisałem cały portal (http://benefitclub.pl/) testując wszystko właśnie MSpec i baaardzo mi owe podejście podpasowało. W sieci niestety nie znajdzie się zbyt wiele materiałów czy tutoriali o MSpec, więc być może temat niedługo rozwinę.
Dziś przedstawię tylko pierwszy krok prac nad projektem – definiowanie wymagań. “Definiowanie wymagań i framework do testów? Jak to?”. A no… tak to. Po kilku chwilach zastanowienia założenia mam takie:
- nauczę się WebAPI udostępniając funkcjonalność przekierowań z “krótkich” URLi na “długie” URLe właśnie tą biblioteką
- przekierowania będę zliczał do RavenDB aby móc sobie powyświetlać statystyki
- obok stworzę “normalną” aplikację MVC, która Angularem pozwoli zarządzać zdefiniowanymi “skrótami” URLi
Na tym etapie mógłbym siąść do kodowania i zapomnieć o Bożym świecie, ale zamiast tego w kilkanaście minut zaprogramowałem sobie testy określające zakres mojej pierwszej “iteracji”, ile ona by miała nie trwać. Przewaga nad normalnym spisaniem założeń w Wordzie czy OneNote jest ogromna: w każdej chwili mogę owe założenia uruchomić i sprawdzić gdzie jestem, co zostało zrobione a co jeszcze do zrobienia pozostało.
Już sam szkielet takiej dokumentacji mówi dużo, nawet jeśli nic tak naprawdę nie sprawdza i nie zawiera żadnego wykonywalnego kodu.
Zresztą zobaczcie sami:
Environment check, when requesting root (1 test), Inconclusive returns success response, Inconclusive: Not implemented API, when requesting existing link for the first time (3 tests), Inconclusive behaves like valid redirection (2 tests), redirects to link target, Inconclusive: Not implemented returns redirect response, Inconclusive: Not implemented adds new visit, Inconclusive: Not implemented API, when requesting existing link for nth time (3 tests), Inconclusive behaves like valid redirection (2 tests), redirects to link target, Inconclusive: Not implemented returns redirect response, Inconclusive: Not implemented adds another visit, Inconclusive: Not implemented API, when requesting nonexisting link (1 test), Inconclusive returns 404 not found, Inconclusive: Not implemented App, when requesting root (2 tests), Inconclusive displays all defined links, Inconclusive: Not implemented returns html, Inconclusive: Not implemented App, when posting new link (2 tests), Inconclusive adds link to database, Inconclusive: Not implemented redirects to links page, Inconclusive: Not implemented App, when posting duplicate link (2 tests), Inconclusive redisplays the same page, Inconclusive: Not implemented shows error message, Inconclusive: Not implemented
To jest wyeksportowany output z test runnera R#. A kod, który spowodował wygenerowanie mi tej listy funkcjonalności, jest banalnie prosty i wygląda tak:
[Subject("Environment check")] public class when_requesting_root { It returns_success_response; } [Behaviors] public class valid_redirection { It returns_redirect_response; It redirects_to_link_target; } [Subject("API")] public class when_requesting_existing_link_for_the_first_time { Behaves_like<valid_redirection> valid_redirection; It adds_new_visit; } [Subject("API")] public class when_requesting_existing_link_for_nth_time { Behaves_like<valid_redirection> valid_redirection; It adds_another_visit; } [Subject("API")] public class when_requesting_nonexisting_link { It returns_404_not_found; } [Subject("App")] public class when_requesting_root { It returns_html; It displays_all_defined_links; } [Subject("App")] public class when_posting_new_link { It adds_link_to_database; It redirects_to_links_page; } [Subject("App")] public class when_posting_duplicate_link { It redisplays_the_same_page; It shows_error_message; }
Wymyślam scenariusz, który chciałbym zaimplementować. Potem zastanawiam się “co ma się w danej sytuacji wydarzyć”. A na koniec spisuję wykorzystując składnię MSpec.
Kolejne kroki to zaimplementowanie ciała takiego testu… i jechanie z TDD w celu osiągnięcia jego funkcjonalności.
I tyle. Nie wiem w sumie co wyniknie z tego posta, może po prostu na nim się skończy i już:). A może w jakiś sposób podrążę dalej w miarę rozwoju tego mikroskopijnego projekciku.
Używał ktoś MSpec? Jeśli tak – podoba się?