1) Wprowadzenie: co to są testy i po co mi to na INF.04?
Wyobraź sobie prosty kalkulator. Dziś działa, ale jutro coś dopiszesz i nagle 2+2 wyjdzie 5. Testy jednostkowe to małe automatyczne programy, które sprawdzają, czy Twoje metody robią to, co trzeba.
Dzięki testom:
- szybko widzisz błędy, zanim oddasz projekt,
- możesz bez strachu zmieniać kod (testy krzykną, jeśli coś zepsujesz),
- na egzaminie INF.04 pokażesz dojrzałe podejście: logika oddzielona od UI + testy logiki.
(To dokładnie ta idea, którą opisujesz już na BiteDu; tu doprecyzowuję i układam wszystko pod lekcję oraz pod MSTest w trybie offline.) bitedu.pl
2) Zasada AAA – najprościej
Każdy test składa się z trzech kroków:
- Arrange – przygotuj dane (np. liczby 2 i 3),
- Act – uruchom metodę (np.
Dodaj(2,3)), - Assert – sprawdź wynik (
5). bitedu.pl
esty jednostkowe w Visual Studio (MSTest) — najprostsze wprowadzenie
Testy jednostkowe to krótkie fragmenty kodu, które sprawdzają, czy pojedyncze elementy programu (najczęściej metody) działają tak, jak powinny.
Dzięki nim szybko łapiesz błędy, zanim pojawią się w aplikacji.
Co można testować?
W testach jednostkowych zwykle testujemy:
1. Metody zwracające wartość
Najprościej, bo możemy sprawdzić: czy wynik jest taki, jaki powinien być?
2. Metody obliczeniowe
Wszelkie kalkulatory, przeliczenia, logika biznesowa.
3. Walidację danych
Czy metoda poprawnie wykrywa złe dane.
4. Funkcje, które mogą rzucać wyjątek
Np. czy metoda rzuci błąd, jeśli ktoś poda 0 jako dzielnik.
5. Metody typu „true/false”
Np. logika sprawdzająca hasło, wiek, czy użytkownik może coś wykonać.
6. Działanie programu przy różnych kombinacjach danych
Testy działają jak automatyczne przykłady: sprawdzają różne przypadki.
Nie testujemy natomiast rzeczy, które są UI (przyciski, okna, style) — bo to nie logika.
Struktura projektu testowego w Visual Studio (MSTest)
Visual Studio tworzy projekt testowy z przykładową klasą:
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Kalkulator.Tests
{
[TestClass]
public class LogikaKalkulatoraTests
{
[TestMethod]
public void Dodawanie_DziałaPoprawnie()
{
}
}
}
Najważniejsze atrybuty i polecenia (MSTest)
Poniżej masz wszystkie podstawowe rzeczy, które uczniowie muszą znać.
[TestClass]
Oznacza klasę, w której znajdują się testy.
[TestClass]
public class LogikaTests
{
}
[TestMethod]
Oznacza pojedynczy test.
[TestMethod]
public void TestDodawania()
{
}
Assert — serce testów
Assert to zestaw poleceń, które sprawdzają, czy wynik metody jest taki, jak powinien.
Najważniejsze polecenia Assert:
1. Assert.AreEqual(expected, actual)
Sprawdza, czy wynik jest zgodny z oczekiwanym.
[TestMethod]
public void Dodaj_2plus3_Zwraca5()
{
int wynik = Logika.Dodaj(2, 3);
Assert.AreEqual(5, wynik);
}
2. Assert.AreNotEqual(expected, actual)
Sprawdza, czy wynik jest różny.
Assert.AreNotEqual(10, Logika.Dodaj(3, 3));
3. Assert.IsTrue(warunek)
Sprawdza, czy warunek jest prawdziwy.
Assert.IsTrue(Logika.SprawdzHaslo("Abc123!"));
4. Assert.IsFalse(warunek)
Assert.IsFalse(Logika.SprawdzHaslo("aaa"));
5. Assert.IsNull(wartość)
Gdy metoda może zwrócić null.
Assert.IsNull(Logika.ZnajdzUzytkownika(-1));
6. Assert.IsNotNull(wartość)
Assert.IsNotNull(Logika.ZnajdzUzytkownika(5));
7. Assert.ThrowsException<T>(metoda)
Sprawdza, czy metoda rzuca wyjątek.
[TestMethod]
public void Dzielenie_PrzezZero_RzucaWyjatek()
{
Assert.ThrowsException<DivideByZeroException>(() => Logika.Podziel(5, 0));
}
Proste przykłady testów dla ucznia
Załóżmy, że masz taką klasę logiki:
public class LogikaKalkulatora
{
public int Dodaj(int a, int b) => a + b;
public int Odejmij(int a, int b) => a - b;
public double Podziel(double a, double b)
{
if (b == 0) throw new DivideByZeroException();
return a / b;
}
public bool CzyDorosly(int wiek) => wiek >= 18;
}
A teraz testy:
[TestClass]
public class LogikaKalkulatoraTests
{
[TestMethod]
public void Dodaj_DwaPlusTrzy_ZwracaPiec()
{
var logika = new LogikaKalkulatora();
int wynik = logika.Dodaj(2, 3);
Assert.AreEqual(5, wynik);
}
[TestMethod]
public void Odejmij_PiecMinusDwa_ZwracaTrzy()
{
var logika = new LogikaKalkulatora();
int wynik = logika.Odejmij(5, 2);
Assert.AreEqual(3, wynik);
}
[TestMethod]
public void Podziel_PrzezZero_RzucaWyjatek()
{
var logika = new LogikaKalkulatora();
Assert.ThrowsException<DivideByZeroException>(() => logika.Podziel(4, 0));
}
[TestMethod]
public void CzyDorosly_Wiek20_ZwracaTrue()
{
var logika = new LogikaKalkulatora();
bool wynik = logika.CzyDorosly(20);
Assert.IsTrue(wynik);
}
}
6) Najważniejsze właściwości i atrybuty testów (MSTest)
[TestClass]– klasa z testami, rozpoznawana przez Test Explorer.[TestMethod]– pojedynczy test.[DataTestMethod]+[DataRow(...)]– (opcjonalnie) jeden test uruchamiany z wieloma zestawami danych.- Asserty (sprawdzacze):
Assert.AreEqual(oczekiwany, wynik)– czy równe,Assert.AreNotEqual,Assert.IsTrue,Assert.IsFalse,Assert.IsNull,Assert.IsNotNull,Assert.Fail()itd. bitedu.pl
7) Podsumowanie – co zapamiętać na INF.04
- Test jednostkowy to mały automat sprawdzający pojedyńczą metodę.
- Trzy kroki AAA: Arrange → Act → Assert.
- W Visual Studio od ręki utworzysz projekt MSTest i uruchomisz testy bez internetu. bitedu.pl
- Oddziel Logika od UI (WPF/MAUI). Testuj Logikę.
- Jeden zestaw testów działa zarówno dla WPF, jak i MAUI, bo testy patrzą na kod w Logika/, a nie na okna.
8) Zadania do samodzielnej pracy
- Dodaj mnożenie i dzielenie
- W
Logika/Kalkulator.csdopiszPomnoz2,Podziel2(zwracaj int; jeśli dzielisz przez 0, zdecyduj: zwróć 0 albo rzuć wyjątek). - Napisz testy MSTest do obu metod, uwzględnij przypadki brzegowe (np. 0, liczby ujemne).
- W
- Dane parametryczne
- Przerób test
Dodaj2_RozneWartosci...tak, by sprawdzał także wartości ujemne i duże liczby (więcej[DataRow]). - Jeśli
[DataTestMethod]nie jest dostępne – napisz 3 osobne[TestMethod].
- Przerób test
- Walidacja wejścia w UI
- Dodaj prostą weryfikację do WPF/MAUI: jeśli pole puste lub nie liczba, pokaż komunikat w Label/TextBlock zamiast liczyć.
- Logika zostaje czysta – walidację robisz w UI albo w oddzielnym helperze w folderze Walidacja.
„https://www.youtube.com/watch?v=ZS95nEXU4-I
Masz już na bitedu.pl testy „podstawowe”:
- poprawne wyniki dla W, Q, AB, AC,
- wyjątki przy dzieleniu przez zero / ujemnym pierwiastku,
- dwa testy dla
Oblicz(W i Q).
Poniżej dorzucam inne testy – takie, które:
- łapią inne przypadki brzegowe,
- sprawdzają ParseDouble,
- domykają testy routera
Oblicz, - testują enum / niepoprawne zadania
- sprawdzają walidację osobno.
1. Dodatkowe testy poprawnych wyników (inne dane)
Formuły z Twojej logiki:
W = (3x - 2) / (2y)Q = ((2x - 3) * y) / zAB = (2x - y)^2 / (3x + 2)AC = √((3x - 7)^2 / (2y))
Kod testów
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using ObliczeniaTablica.Logika;
namespace ObliczeniaTablica.Tests
{
[TestClass]
public class ObliczeniaRozszerzoneTests
{
[TestMethod]
public void W_DlaUjemnegoX_ZwracaPoprawnyWynik()
{
// (3 * (-1) - 2) / (2 * 2) = (-3 - 2) / 4 = -5/4 = -1.25
double wynik = LogikaObliczen.W(-1, 2);
Assert.AreEqual(-1.25, wynik, 1e-9);
}
[TestMethod]
public void Q_DlaUjemnegoY_ZwracaPoprawnyWynik()
{
// x=5, y=-2, z=1
// ((2*5 - 3) * (-2)) / 1 = (7 * -2) / 1 = -14
double wynik = LogikaObliczen.Q(5, -2, 1);
Assert.AreEqual(-14.0, wynik, 1e-9);
}
[TestMethod]
public void AB_GdyLicznikZero_ZwracaZero()
{
// (2x - y)^2 / (3x + 2)
// Wybieramy y = 2x → (2x - 2x)^2 = 0
// np. x=2, y=4: mianownik = 3*2+2 = 8, wynik = 0/8 = 0
double wynik = LogikaObliczen.AB(2, 4);
Assert.AreEqual(0.0, wynik, 1e-9);
}
[TestMethod]
public void AC_GdyLicznikZero_ZwracaZero()
{
// AC = sqrt( (3x - 7)^2 / (2y) )
// chcemy (3x - 7) = 0 → x = 7/3
// wtedy licznik = 0, cokolwiek w mianowniku (≠0) da 0 pod pierwiastkiem
double x = 7.0 / 3.0;
double y = -2.0; // specjalnie ujemne, ale ≠ 0
double wynik = LogikaObliczen.AC(x, y);
Assert.AreEqual(0.0, wynik, 1e-9);
}
}
}
Co tu sprawdzamy?
W_DlaUjemnegoX_...– czy wzór poprawnie działa, gdyxjest ujemne.Q_DlaUjemnegoY_...– zachowanie przy ujemnymy.AB_GdyLicznikZero_...– przypadek, w którym licznik jest 0, ale mianownik ≠ 0 → wynik powinien być dokładnie 0.AC_GdyLicznikZero_...– ciekawy edge case: licznik 0, mianownik ujemny →0 / (cokolwiek ≠ 0) = 0, pierwiastek z 0 = 0, bez wyjątku.
To są inne przypadki niż w artykule, ale ładnie pokazują różne zachowania wzorów.
2. Testy Oblicz dla wszystkich zadań + niepoprawnego enum
W artykule testujesz Oblicz tylko dla W i Q. Domkniemy temat:
Kod
namespace ObliczeniaTablica.Tests
{
[TestClass]
public class ObliczeniaRouterTests
{
[TestMethod]
public void Oblicz_AB_ZwracaToSamoCoAB()
{
double x = 2, y = 4;
double oczekiwany = LogikaObliczen.AB(x, y);
double wynik = LogikaObliczen.Oblicz(Zadanie.AB, x, y, z: 0);
Assert.AreEqual(oczekiwany, wynik, 1e-12);
}
[TestMethod]
public void Oblicz_AC_ZwracaToSamoCoAC()
{
double x = 3, y = 2;
double oczekiwany = LogikaObliczen.AC(x, y);
double wynik = LogikaObliczen.Oblicz(Zadanie.AC, x, y, z: 0);
Assert.AreEqual(oczekiwany, wynik, 1e-12);
}
[TestMethod]
public void Oblicz_NieznaneZadanie_RzucaArgumentOutOfRange()
{
// Tworzymy "fałszywy" enum spoza zakresu 1–4
Zadanie zadanie = (Zadanie)999;
Assert.ThrowsException<ArgumentOutOfRangeException>(
() => LogikaObliczen.Oblicz(zadanie, 1, 2, 3)
);
}
}
}
Co tu się dzieje?
Oblicz_AB_.../Oblicz_AC_...– sprawdzają, czy routerOblicznaprawdę wywołuje właściwą metodę.
Robimy tak:- liczmy wynik bezpośrednio (
AB,AC), - potem przez
Oblicz, - porównujemy.
- liczmy wynik bezpośrednio (
Oblicz_NieznaneZadanie_...– sprawdzamy gałąźdefaultwswitch(_ => throw new ArgumentOutOfRangeException).
Wymuszamy to przez rzutowanie liczby999na typZadanie.
3. Testy ParseDouble – format liczby
Masz w logice fajną metodę:
public static double ParseDouble(string input)
{
if (double.TryParse(input.Replace(',', '.'),
NumberStyles.Float,
CultureInfo.InvariantCulture,
out double val))
return val;
throw new FormatException("Niepoprawna liczba.");
}
Idealny kandydat pod testy:
Kod
using System.Globalization;
namespace ObliczeniaTablica.Tests
{
[TestClass]
public class ParseDoubleTests
{
[TestMethod]
public void ParseDouble_AkceptujeKropke()
{
double wynik = LogikaObliczen.ParseDouble("2.5");
Assert.AreEqual(2.5, wynik, 1e-9);
}
[TestMethod]
public void ParseDouble_AkceptujePrzecinek()
{
double wynik = LogikaObliczen.ParseDouble("3,14");
Assert.AreEqual(3.14, wynik, 1e-9);
}
[TestMethod]
public void ParseDouble_IgnorujeSpacjeNaBrzegach()
{
double wynik = LogikaObliczen.ParseDouble(" 10,5 ");
Assert.AreEqual(10.5, wynik, 1e-9);
}
[TestMethod]
public void ParseDouble_NieprawidlowaLiczba_RzucaFormatException()
{
Assert.ThrowsException<FormatException>(
() => LogikaObliczen.ParseDouble("abc123")
);
}
}
}
Wyjaśnienie
- Pierwsze trzy testy pokazują uczniowi, że:
- można wpisać liczbę z kropką,
- można użyć przecinka,
- można mieć spacje przed i po liczbie (TryParse z
NumberStyles.Floatto łyknie).
- Ostatni test uczy, że:
- śmieci w polu wejściowym (
"abc123") powodująFormatException, - czyli to jest moment, w którym leci Twój
catch (FormatException)wProgram.Main().
- śmieci w polu wejściowym (
4. Testy WalidacjaWejscia.RzucGdyZero osobno
Ta klasa też jest częścią logiki. Można ją przetestować bezpośrednio, żeby uczniowie zobaczyli, że walidacja też jest normalnym kodem, który testujemy.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using ObliczeniaTablica.Walidacja;
namespace ObliczeniaTablica.Tests
{
[TestClass]
public class WalidacjaWejsciaTests
{
[TestMethod]
public void RzucGdyZero_GdyZero_RzucaArgumentException()
{
Assert.ThrowsException<ArgumentException>(
() => WalidacjaWejscia.RzucGdyZero(0.0, "y")
);
}
[TestMethod]
public void RzucGdyZero_GdyNieZero_NieRzuca()
{
// Jeżeli metoda rzuci wyjątek, test automatycznie "padnie".
WalidacjaWejscia.RzucGdyZero(5.0, "y");
}
}
}

