Dataniedziela, 9 sierpnia 2026 Czas07:48:08
← Testowanie i dokumentowanie aplikacji

Lekcja 10. Zadanie kontrolne — klasa z logiką i pełny zestaw testów jednostkowych

Trudny / egzaminacyjny

Po co się tego uczymy?

To podsumowanie całego działu: napiszesz od podstaw klasę z logiką biznesową i KOMPLETNY zestaw testów jednostkowych — dokładnie w takim stylu, w jakim może pojawić się zadanie na egzaminie praktycznym INF.04. Zadanie sprawdza, czy potrafisz samodzielnie zaprojektować przypadki testowe (typowe, brzegowe, błędne), a nie tylko odtworzyć gotowy przykład z lekcji.

Teoria

Specyfikacja zadania. Zaprojektuj i zaimplementuj klasę RezerwacjaSali obsługującą rezerwacje sal konferencyjnych w firmie, wraz z KOMPLETNYM zestawem testów jednostkowych (MSTest lub xUnit — do wyboru). Wymagania funkcjonalne:

  • Metoda CzyDostepna(DateTime poczatek, DateTime koniec, List<Rezerwacja> istniejaceRezerwacje) zwracająca bool — sala jest DOSTĘPNA, jeśli nowy termin NIE nakłada się (w żadnym punkcie) z żadną z istniejących rezerwacji.
  • Metoda DodajRezerwacje(DateTime poczatek, DateTime koniec, string osoba) — dodaje nową rezerwację, ale RZUCA wyjątek InvalidOperationException, jeśli termin koliduje z istniejącą rezerwacją.
  • Walidacja: koniec musi być PÓŹNIEJSZY niż poczatek (w przeciwnym razie ArgumentException); rezerwacja nie może trwać dłużej niż 8 godzin.
  • Metoda LiczbaRezerwacjiWDniu(DateTime dzien) zwracająca liczbę rezerwacji przypadających na wskazany dzień.

Wymagania dotyczące testów (to jest właściwe zadanie kontrolne — ocena dotyczy GŁÓWNIE jakości i kompletności testów, nie samej implementacji):

  1. Zastosuj zasadę AAA (Arrange-Act-Assert) w KAŻDYM teście.
  2. Pokryj testami przypadek TYPOWY (poprawna rezerwacja, brak kolizji).
  3. Pokryj testami przypadki BRZEGOWE: rezerwacja kończąca się DOKŁADNIE w momencie rozpoczęcia innej (czy to kolizja?), rezerwacja trwająca dokładnie 8 godzin (graniczna, dozwolona) i 8 godzin + 1 minuta (niedozwolona).
  4. Pokryj testami przypadki BŁĘDNE: koniec wcześniejszy niż poczatek, próba dodania rezerwacji kolidującej z istniejącą.
  5. Nazwij testy zgodnie z konwencją Metoda_Scenariusz_OczekiwanyWynik (lekcja 2).
  6. Napisz krótką dokumentację (komentarz XML doc, lekcja 8) dla metody CzyDostepna.

Schemat

RezerwacjaSali
 ├── CzyDostepna(poczatek, koniec, istniejące) : bool
 ├── DodajRezerwacje(poczatek, koniec, osoba)   : rzuca InvalidOperationException przy kolizji
 └── LiczbaRezerwacjiWDniu(dzien)                : int

PRZYPADKI DO PRZETESTOWANIA:
  ✓ typowy: 10:00-11:00 vs istniejąca 14:00-15:00 → dostępna
  ⚠ brzegowy: 10:00-11:00 vs istniejąca 11:00-12:00 → dostępna (styk, nie nakładanie)
  ⚠ brzegowy: rezerwacja dokładnie 8h → dozwolona
  ✗ błędny: koniec przed początkiem → ArgumentException
  ✗ błędny: 9:00-18:01 (8h 1min) → ArgumentException
  ✗ błędny: DodajRezerwacje z kolizją → InvalidOperationException

Przykład z życia

Systemy rezerwacji sal konferencyjnych (Outlook, Google Calendar Rooms) codziennie rozwiązują dokładnie ten problem: sprawdzają, czy proponowany termin NIE nakłada się z żadną istniejącą rezerwacją, uwzględniając subtelne przypadki brzegowe — czy spotkanie kończące się dokładnie o 11:00 koliduje z innym zaczynającym się o 11:00 (zwykle NIE — to "styk", nie nakładanie). Właśnie takie subtelności są source większości błędów w prawdziwych systemach rezerwacji i dlatego wymagają starannie zaprojektowanych testów.

RezerwacjaSali.cs (szkielet do samodzielnego uzupełnienia)

// Szkielet do uzupełnienia - to punkt wyjścia, nie gotowe rozwiązanie
using System;
using System.Collections.Generic;
using System.Linq;

public class Rezerwacja
{
    public DateTime Poczatek { get; set; }
    public DateTime Koniec { get; set; }
    public string Osoba { get; set; }
}

public class RezerwacjaSali
{
    private List<Rezerwacja> _rezerwacje = new List<Rezerwacja>();

    public bool CzyDostepna(DateTime poczatek, DateTime koniec, List<Rezerwacja> istniejaceRezerwacje)
    {
        // TODO: zaimplementuj sprawdzenie nakładania się przedziałów czasowych.
        // Wskazówka: dwa przedziały [A_start, A_end) i [B_start, B_end) NIE nakładają się,
        // gdy A_end <= B_start LUB B_end <= A_start.
        throw new NotImplementedException();
    }

    public void DodajRezerwacje(DateTime poczatek, DateTime koniec, string osoba)
    {
        // TODO: zwaliduj dane wejściowe (koniec > poczatek, maks. 8h),
        // sprawdź kolizję przez CzyDostepna, dodaj do _rezerwacje albo rzuć wyjątek.
        throw new NotImplementedException();
    }

    public int LiczbaRezerwacjiWDniu(DateTime dzien)
    {
        // TODO: policz, ile rezerwacji z _rezerwacje przypada na wskazany dzień.
        throw new NotImplementedException();
    }
}

Komentarz i wyjaśnienie kodu

Podpowiedź w komentarzu (A_end <= B_start LUB B_end <= A_start) to klasyczny wzór na sprawdzenie NIE-nakładania się dwóch przedziałów czasowych — łatwiej sprawdzić warunek BRAKU kolizji i go zanegować, niż próbować wypisać wszystkie warianty nakładania się wprost. To dobra ogólna technika przy projektowaniu logiki z wieloma warunkami: czasem prościej sformułować i zanegować przeciwieństwo.

Rzucanie NotImplementedException() w szkielecie to celowy sygnał "tu musisz dopisać kod" — to standardowa praktyka przy zadaniach kontrolnych i szkieletach projektów, żeby jasno oznaczyć, co jest do zrobienia.

Ćwiczenie samodzielne

Zaimplementuj metodę CzyDostepna zgodnie z podpowiedzią, a następnie napisz do niej co najmniej 3 testy: typowy przypadek bez kolizji, typowy przypadek Z kolizją, przypadek brzegowy "styku" (koniec jednej rezerwacji = początek drugiej).

Zadania do pracy własnej

  1. (4 pkt) Zaimplementuj LiczbaRezerwacjiWDniu i napisz dla niej 3 testy: dzień bez żadnej rezerwacji (0), dzień z jedną rezerwacją (1), dzień z kilkoma rezerwacjami (poprawna suma). Nazwij testy zgodnie z konwencją z lekcji 2.

  2. (4 pkt) Zaimplementuj walidację w DodajRezerwacje (koniec > poczatek, maksymalnie 8 godzin) i napisz testy sprawdzające oba warunki, w tym PRZYPADEK BRZEGOWY dokładnie 8-godzinnej rezerwacji (powinna być dozwolona) oraz 8 godzin i 1 minutę (powinna zostać odrzucona).

  3. (4 pkt) Dokończ pełną implementację CzyDostepna i DodajRezerwacje (z rzucaniem InvalidOperationException przy kolizji), a następnie napisz KOMPLETNY zestaw co najmniej 8 testów pokrywających: typowy brak kolizji, typową kolizję, "styk" na początku i na końcu (2 osobne testy), próbę dodania kolidującej rezerwacji (sprawdź wyjątek), oraz dodaj komentarz XML doc do CzyDostepna. Łącznie zadanie warte 12 punktów — analogicznie do zadań kontrolnych w innych działach tego kursu.

Typowe błędy

Sprawdzanie kolizji przez porównanie WYŁĄCZNIE godzin rozpoczęcia (pomijając czas trwania) — dwie rezerwacje mogą zaczynać się o różnych godzinach, a mimo to się nakładać (np. 9:00-12:00 i 10:00-11:00) — trzeba porównywać CAŁE przedziały czasowe, nie same momenty startu.

Traktowanie "styku" (koniec jednej = początek drugiej) jako kolizji — to częsty błąd projektowy; w większości systemów rezerwacji spotkanie kończące się o 11:00 NIE koliduje z innym zaczynającym się dokładnie o 11:00. Warto to jawnie przetestować, żeby uniknąć niejednoznaczności.

Pisanie testów DOPIERO po napisaniu całej implementacji, "na wyczucie co powinno działać" — lepszą praktyką (choć niewymaganą w tym zadaniu) jest myślenie o przypadkach testowych RÓWNOLEGLE z projektowaniem logiki, co pomaga wychwycić niejasności w specyfikacji (jak właśnie kwestia "styku") ZANIM napiszesz błędny kod.

Nawiązanie do egzaminu zawodowego

To zadanie kontrolne podsumowuje CAŁY dział INF.04.8: teorię testowania (lekcja 1), praktyczne MSTest (lekcja 2), zastosowanie w różnych typach aplikacji (lekcje 3-5), testy niefunkcjonalne (lekcja 6), organizację procesu (lekcja 7), dokumentację (lekcja 8) i automatyzację (lekcja 9). Umiejętność zaprojektowania KOMPLETNEGO zestawu testów (typowych, brzegowych, błędnych) dla danej specyfikacji to dokładnie to, czego oczekuje się na egzaminie praktycznym INF.04, gdzie temat testów jednostkowych pojawiał się już w poprzednich sesjach egzaminacyjnych.