Przypadki testowe, dane brzegowe, assert, debugowanie kodu
ð Podstawa programowa: II.1+I.3Kazdy program moze zawierac bledy (ang. bugs). Testowanie to proces sprawdzania, czy program dziala poprawnie dla roznych danych wejsciowych. Nawet jesli program kompiluje sie i uruchamia bez bledow, moze zwracac nieprawidlowe wyniki dla niektorych danych.
Instrukcja assert w Pythonie sprawdza, czy warunek jest prawdziwy. Jesli nie - program przerywa dzialanie z bledem AssertionError. To najprostszy sposob testowania:
# Funkcja do przetestowania
def silnia(n):
if n < 0:
return None
wynik = 1
for i in range(1, n + 1):
wynik *= i
return wynik
# Testy z uzyciem assert
assert silnia(0) == 1, "Silnia z 0 powinna wynosic 1"
assert silnia(1) == 1, "Silnia z 1 powinna wynosic 1"
assert silnia(5) == 120, "Silnia z 5 powinna wynosic 120"
assert silnia(10) == 3628800, "Silnia z 10 powinna wynosic 3628800"
assert silnia(-1) == None, "Silnia z liczby ujemnej powinna zwrocic None"
print("Wszystkie testy przeszly!")
def jest_palindromem(tekst):
tekst = tekst.lower().replace(" ", "")
return tekst == tekst[::-1]
# Testy - rozne przypadki
assert jest_palindromem("kajak") == True # typowy palindrom
assert jest_palindromem("Kajak") == True # wielkie litery
assert jest_palindromem("python") == False # nie-palindrom
assert jest_palindromem("") == True # pusty tekst (brzegowy)
assert jest_palindromem("a") == True # jeden znak (brzegowy)
assert jest_palindromem("aa") == True # dwa takie same znaki
assert jest_palindromem("ab") == False # dwa rozne znaki
assert jest_palindromem("A ba") == True # spacje w srodku
print("Wszystkie testy palindromu OK!")
Gdy program nie dziala poprawnie, musimy znalezc blad. Sa dwie glowne metody:
def znajdz_max(lista):
if len(lista) == 0:
return None
maks = lista[0]
print(f"Start: maks = {maks}") # debug
for i in range(1, len(lista)):
print(f" Porownuje {maks} z {lista[i]}") # debug
if lista[i] > maks:
maks = lista[i]
print(f" Nowe maks = {maks}") # debug
return maks
wynik = znajdz_max([3, 7, 2, 9, 1])
print(f"Wynik: {wynik}")
Wieksznosc srodowisk (PyCharm, VS Code, IDLE) posiada wbudowany debugger, ktory pozwala:
Programowanie defensywne polega na zabezpieczaniu kodu przed niepoprawnymi danymi wejsciowymi:
def dzielenie(a, b):
"""Dzieli a przez b z zabezpieczeniem."""
if not isinstance(a, (int, float)):
raise TypeError("Argument a musi byc liczba")
if not isinstance(b, (int, float)):
raise TypeError("Argument b musi byc liczba")
if b == 0:
raise ValueError("Nie mozna dzielic przez zero!")
return a / b
# Testy
assert dzielenie(10, 2) == 5.0
assert dzielenie(7, 3) == 7/3
assert dzielenie(-10, 2) == -5.0
# Te wywolania powinny rzucic bledy:
try:
dzielenie(10, 0)
except ValueError as e:
print(f"Blad: {e}") # Blad: Nie mozna dzielic przez zero!
try:
dzielenie("abc", 2)
except TypeError as e:
print(f"Blad: {e}") # Blad: Argument a musi byc liczba
Dana jest funkcja srednia(lista), ktora oblicza srednia arytmetyczna listy liczb. Napisz co najmniej 6 testow z uzyciem assert, obejmujacych dane typowe, brzegowe i specjalne.
def srednia(lista):
if len(lista) == 0:
return 0
return sum(lista) / len(lista)
# Testy
assert srednia([1, 2, 3, 4, 5]) == 3.0, "Srednia z 1-5 to 3"
assert srednia([10]) == 10.0, "Jeden element"
assert srednia([0, 0, 0]) == 0.0, "Same zera"
assert srednia([-5, 5]) == 0.0, "Liczby ujemne i dodatnie"
assert srednia([]) == 0, "Pusta lista"
assert srednia([1000000, 2000000]) == 1500000.0, "Duze liczby"
assert srednia([1, 1, 1, 1]) == 1.0, "Identyczne elementy"
assert abs(srednia([1, 2]) - 1.5) < 0.001, "Wynik ulamkowy"
print("Wszystkie testy sredniej przeszly!")
Ponizsze funkcje zawieraja bledy. Uzyj testow i debugowania, aby je znalezc i naprawic:
# Funkcja A - powinna zwracac True jesli n jest parzyste
def jest_parzysta(n):
return n % 2 == 1 # BUG!
# Funkcja B - powinna zwracac liste bez duplikatow
def usun_duplikaty(lista):
wynik = []
for el in lista:
if el in wynik: # BUG!
wynik.append(el)
return wynik
# Funkcja C - powinna odwracac tekst
def odwroc(tekst):
wynik = ""
for i in range(len(tekst)): # BUG!
wynik += tekst[i]
return wynik
# Naprawiona funkcja A
def jest_parzysta(n):
return n % 2 == 0 # zmieniono 1 na 0
# Naprawiona funkcja B
def usun_duplikaty(lista):
wynik = []
for el in lista:
if el not in wynik: # dodano "not"
wynik.append(el)
return wynik
# Naprawiona funkcja C
def odwroc(tekst):
wynik = ""
for i in range(len(tekst) - 1, -1, -1): # iteracja od konca
wynik += tekst[i]
return wynik
# Testy naprawionych funkcji
assert jest_parzysta(4) == True
assert jest_parzysta(7) == False
assert jest_parzysta(0) == True
assert usun_duplikaty([1, 2, 2, 3, 3, 3]) == [1, 2, 3]
assert usun_duplikaty([]) == []
assert usun_duplikaty([1]) == [1]
assert odwroc("abc") == "cba"
assert odwroc("") == ""
assert odwroc("a") == "a"
print("Wszystkie naprawione funkcje dzialaja!")
Napisz funkcje bezpieczna_potega(podstawa, wykladnik), ktora oblicza potege z pelnym zabezpieczeniem: sprawdzanie typow, walidacja zakresu (wykladnik >= 0, wykladnik <= 1000), obsluga wyjatkow. Dodaj testy.
def bezpieczna_potega(podstawa, wykladnik):
"""Oblicza podstawa^wykladnik z walidacja danych."""
# Sprawdzenie typow
if not isinstance(podstawa, (int, float)):
raise TypeError(f"Podstawa musi byc liczba, otrzymano: {type(podstawa)}")
if not isinstance(wykladnik, int):
raise TypeError(f"Wykladnik musi byc liczba calkowita, otrzymano: {type(wykladnik)}")
# Sprawdzenie zakresu
if wykladnik < 0:
raise ValueError("Wykladnik nie moze byc ujemny")
if wykladnik > 1000:
raise ValueError("Wykladnik zbyt duzy (max 1000)")
return podstawa ** wykladnik
# Testy poprawnych wywolan
assert bezpieczna_potega(2, 0) == 1
assert bezpieczna_potega(2, 10) == 1024
assert bezpieczna_potega(0, 5) == 0
assert bezpieczna_potega(3, 3) == 27
assert bezpieczna_potega(-2, 3) == -8
assert bezpieczna_potega(1.5, 2) == 2.25
# Testy blednych danych
import sys
try:
bezpieczna_potega("abc", 2)
assert False, "Powinien rzucic TypeError"
except TypeError:
pass # OK
try:
bezpieczna_potega(2, -1)
assert False, "Powinien rzucic ValueError"
except ValueError:
pass # OK
try:
bezpieczna_potega(2, 1001)
assert False, "Powinien rzucic ValueError"
except ValueError:
pass # OK
print("Wszystkie testy bezpiecznej potegi przeszly!")
Napisz zestaw testow dla funkcji sortowania babelkowego. Przetestuj: pusta lista, jeden element, juz posortowana, odwrotnie posortowana, duplikaty, liczby ujemne, duza lista losowa (porownaj z wbudowanym sorted()).
import random
def sortuj_babelkowo(lista):
lista = lista.copy()
n = len(lista)
for i in range(n - 1):
for j in range(n - 1 - i):
if lista[j] > lista[j + 1]:
lista[j], lista[j + 1] = lista[j + 1], lista[j]
return lista
# Test 1: Pusta lista
assert sortuj_babelkowo([]) == [], "Pusta lista"
# Test 2: Jeden element
assert sortuj_babelkowo([42]) == [42], "Jeden element"
# Test 3: Juz posortowana
assert sortuj_babelkowo([1, 2, 3, 4, 5]) == [1, 2, 3, 4, 5], "Posortowana"
# Test 4: Odwrotnie posortowana
assert sortuj_babelkowo([5, 4, 3, 2, 1]) == [1, 2, 3, 4, 5], "Odwrotnie"
# Test 5: Z duplikatami
assert sortuj_babelkowo([3, 1, 3, 2, 1]) == [1, 1, 2, 3, 3], "Duplikaty"
# Test 6: Liczby ujemne
assert sortuj_babelkowo([-3, 5, -1, 0, 2]) == [-3, -1, 0, 2, 5], "Ujemne"
# Test 7: Dwa elementy
assert sortuj_babelkowo([2, 1]) == [1, 2], "Dwa elementy"
# Test 8: Identyczne elementy
assert sortuj_babelkowo([7, 7, 7]) == [7, 7, 7], "Identyczne"
# Test 9: Duza lista losowa
losowa = [random.randint(-1000, 1000) for _ in range(100)]
assert sortuj_babelkowo(losowa) == sorted(losowa), "Losowa lista 100 elementow"
# Test 10: Porownanie z sorted() na wielu probkach
for _ in range(20):
test = [random.randint(-50, 50) for _ in range(random.randint(0, 30))]
assert sortuj_babelkowo(test) == sorted(test), f"Losowy test: {test}"
print("Wszystkie testy sortowania przeszly! (10+ przypadkow)")