Średni · lekcja 3
Testy jednostkowe w aplikacji desktopowej WPF
Aplikacja WPF ma okna, przyciski i pola tekstowe — a testów jednostkowych NIE piszemy dla wyglądu interfejsu. Sekret polega na tym, żeby logikę biznesową (obliczenia, walidację, reguły) trzymać w OSOBNEJ warstwie, niezależnej od okien — dokładnie tak, jak robi...
Zacznij tutaj
Po co się tego uczymy?
Wprowadzenie
Teoria
Krok po kroku — budowa rozwiązania z trzema projektami:
- Otwórz istniejący projekt WPF (albo utwórz nowy jak w lekcji o pierwszej aplikacji WPF, dział Aplikacje desktopowe).
- Dodaj bibliotekę logiki: prawym przyciskiem na ROZWIĄZANIE → Dodaj → Nowy projekt → wyszukaj
Class Library→ wybierz "Biblioteka klas (.NET)" → nazwij np.KalkulatorApp.Logika→ Utwórz. - Podepnij bibliotekę do aplikacji WPF: prawym przyciskiem na projekt WPF → Dodaj → Odwołanie do projektu… → zaznacz
KalkulatorApp.Logika→ OK. - Dodaj projekt testowy: prawym przyciskiem na ROZWIĄZANIE → Dodaj → Nowy projekt → wyszukaj
MSTest→ nazwij np.KalkulatorApp.Tests→ Utwórz. - Podepnij projekt testowy WYŁĄCZNIE do biblioteki Logika (nie do WPF!): prawym przyciskiem na
KalkulatorApp.Tests→ Dodaj → Odwołanie do projektu… → zaznaczKalkulatorApp.Logika→ OK. - Przenieś (albo od razu napisz) klasy z logiką biznesową w projekcie Logika, testy w projekcie Tests, uruchom przez Test → Uruchom wszystkie testy.
Kluczowa zasada: testujemy Logikę, nie UI. Okna, przyciski, kolory, style — to wygląd, nie zachowanie, i nie da się (ani nie warto) tego sensownie testować jednostkowo. Natomiast KAŻDA reguła biznesowa (obliczenia, walidacja, decyzje "co się stanie, gdy...") powinna dać się przetestować BEZ uruchamiania jakiegokolwiek okna.
Jak to osiągnąć — oddzielenie Logiki od UI (przypomnienie z wzorca MVVM). Cała logika biznesowa trafia do zwykłych klas C# (np. w folderze/projekcie Logika/), które NIE mają żadnego odwołania do WPF, okien czy kontrolek. ViewModel korzysta z tych klas i WYSTAWIA ich wyniki do UI przez bindowanie, ale sam nie zawiera skomplikowanej logiki — tylko "klej" łączący Logikę z Widokiem.
Co można testować w takiej architekturze:
- Metody zwracające wartość — czy wynik jest poprawny (np. obliczenia kalkulatora).
- Metody obliczeniowe — wszelkie przeliczenia, logika biznesowa.
- Walidację danych — czy metoda poprawnie wykrywa błędne dane wejściowe.
- Funkcje rzucające wyjątek — np. czy dzielenie przez zero rzuca
DivideByZeroException. - Metody typu true/false — np. logika sprawdzająca hasło, wiek, uprawnienia.
Struktura projektu z warstwą testowalną. Typowe rozwiązanie w Visual Studio: trzy projekty w jednym rozwiązaniu — NazwaAplikacji (właściwa aplikacja WPF z oknami XAML), NazwaAplikacji.Logika (biblioteka klas z czystą logiką, bez zależności od WPF) i NazwaAplikacji.Tests (projekt MSTest z odwołaniem TYLKO do biblioteki Logika — nie musi w ogóle "widzieć" WPF).
Dodatkowe polecenia MSTest — dane parametryczne. [DataTestMethod] razem z [DataRow(...)] pozwala uruchomić JEDEN test z WIELOMA zestawami danych wejściowych, zamiast kopiować niemal identyczny test kilka razy — np. sprawdzenie tej samej metody dla kilku różnych par liczb.
Zobacz zależności
Schemat
Rozwiązanie WPF (Solution)
├── KalkulatorApp/ (WPF: okna XAML, MainWindow.xaml.cs)
│ └── odwołuje się do ↓
├── KalkulatorApp.Logika/ (biblioteka klas, BEZ odwołania do WPF)
│ └── LogikaKalkulatora.cs
└── KalkulatorApp.Tests/ (MSTest, odwołanie TYLKO do Logika)
└── LogikaKalkulatoraTests.cs
MainWindow.xaml ──bindowanie──▶ ViewModel ──wywołuje──▶ LogikaKalkulatora
(UI, NIE testujemy) (cienki "klej") (logika, TESTUJEMY tutaj)
Połącz z praktyką
Przykład z życia
Wyobraź sobie kalkulator: dziś działa poprawnie, ale jutro dopiszesz nową funkcję i nagle 2+2 daje 5. Bez testów zauważysz to dopiero, gdy ktoś zgłosi błąd — z testami dowiesz się o tym NATYCHMIAST po zmianie kodu, jeszcze zanim ktokolwiek uruchomi aplikację. To dokładnie ta sama korzyść, którą testy dają profesjonalnym zespołom programistycznym: możliwość zmiany kodu BEZ STRACHU, że coś po cichu przestało działać.