Na blogu oraz MSDN pisałem niejednokrotnie o testach jednostkowych oraz integracyjnych. Ostatnio jednak zastanawiałem się nad podstawowym problemem – jak zorganizować to od strony struktury katalogowej? Musimy rozważyć następujące problemy:
- Testy powinny być w każdej chwili dostępnie do odpalenia i weryfikacji.
- Wykonując prostą refaktoryzację (zmiana nazwy klasy), automatycznie nazwa powinna zaktualizować się w testach.
- Testy jednostkowe stanowią dobrą dokumentację oraz instrukcję używania zaimplementowanej funkcjonalności. Z tego względu każdy z developerów z łatwością powinien odnaleźć dowolne projekty testów zanim przejdzie do konkretnej specyfikacji. Moim zdaniem jeśli ktoś chce zobaczyć co robi kod, powinien zacząć od przeczytania testów a nie właściwego kodu.
- Zbyt wysoka liczba projektów w solucji ZNACZĄCO obniża wydajność VS.
- Projekty zawierające testy będą odpalanie automatycznie na serwerze w jakiś odstępach czasu.
Zdecydowałem się podzielić testy na dwie grupy – wymagające konfiguracji oraz te, wykonywane w całości w pamięci. Warto zaznaczyć, że nie mam podziału na testy integracyjne oraz jednostkowe. O ile test integracyjny nie wymaga zasobów poza pamięcią (bazy danych, usługi itp.) lub jakieś konfiguracji (np. nazwa portu, ip address) wciąż przechowuje je w projekcie z innymi testami jednostkowymi z tym, że w odpowiednim namespace. Wciąż mogą być w każdej chwili odpalane i weryfikowane ponieważ nie wymagają jakiejkolwiek konfiguracji. Czyli jeśli mam test integracyjny który łączy klasę A z klasą B (połączenie jest in-memory), umieszczam go w odpowiednim namespace w testach jednostkowych.
Testy jednostkowe powinny być na bieżącą odpalane i sprawdzanie przez programistów (poza automatycznym uruchamianiem przez buildmachine). Aby to ułatwić wszystkie projekty testów umieszczam w osobnym folderze. Proponuje podział na trzy foldery: Production Code, In-Memory tests oraz Configurable tests. Na przykład:
Production Code:
-
SampleApp.Presentation.Behaviour.csproj
-
SampleApp.Presentation.Views.csproj
-
SampleApp.BusinessLayer.csproj
-
Device1 (folder)
-
SampleDevice.csproj
In-Memory Tests:
-
AllTests.sln (opcjonalne)
-
RunAll (opcjonalne)
-
SampleApp.Presentation.Behaviour.Test.csproj
-
SampleApp.BusinessLayer.Test.csproj
-
Device1 (folder)
-
SampleDevice.Testcsproj
Configurable Tests – analogicznie do In-Memory Tests
Struktura katalogów w In-Memory Tests jest taka sama jak w Production Code – z tym, że zamiast właściwego kodu, projekty zawierają wyłącznie testy. Opcjonalnie tworze AllTests.sln, który ułatwia ewentualne debugowanie (np. odpalamy wszystkie unit testy i chcemy sprawdzić co dokładnie popsuło się w którymś z nich).
Warto również w solucjach w Producton Code dodać referencje do projektu z testami. Dzięki temu jakakolwiek refaktoryzacja na kodzie, zostanie również wykonana na testach (np. zmiana klasy, kolejności parametrów itp.). Wadą tego jest oczywiście spowolnienie solucji i dlatego mam wątpliwości – w dużych solucjach naprawdę każdy dodany projekt znaczący ma wpływ na wydajność.
Jak widać jestem zwolennikiem tworzenia nowego projektu z testami dla każdego projektu. Uważam, że bardzo ułatwia to czytanie (testy jako dokumentacja). W In-Memory Tests można również umieścić jakiś skrypt uruchamiający wszystkie testy – taki szybki test dla programistów, którzy zmodyfikowali coś w Production Code.
Podział na foldery a nie mieszanie projektów z testami z projektami produkcyjnymi w jednym folderze, ułatwia znalezienie konkretnego testu (szczególnie nowym programistom).