Cześć! W poprzedniej lekcji utworzyliśmy izolowany projekt z .venv, pip i requirements.txt. Masz więc bezpieczne miejsce, w którym można uruchamiać krótkie programy i później budować testy.
Teraz przechodzimy do struktur danych: list, słowników, zbiorów i krotek. W automatyzacji testów nie są one tylko składnią Pythona. To sposób na precyzyjne modelowanie danych wejściowych, odpowiedzi API, oczekiwanych rezultatów, uprawnień i zestawów scenariuszy. Po tej lekcji będziesz umieć dobrać strukturę do tego, co dane znaczą, a nie tylko do tego, która składnia akurat wygląda znajomo.
Najpierw znaczenie danych, potem nawiasy
Wszystkie cztery struktury przechowują wiele wartości, ale odpowiadają na różne potrzeby. Przy wyborze zadaj sobie cztery pytania:
- Czy kolejność elementów ma znaczenie?
- Czy ten sam element może wystąpić wiele razy?
- Czy dane będą się zmieniały w trakcie działania programu?
- Czy odczytuję wartość po pozycji, czy pod opisową nazwą?
Oto praktyczna mapa decyzji:
| Gdy dane są… | Najczęściej wybierz | Przykład z testów |
|---|---|---|
| uporządkowaną sekwencją, która może się zmieniać i zawierać powtórzenia | list | kolejne kroki testu, elementy zamówienia, scenariusze |
| opisane nazwami pól | dict | dane użytkownika, odpowiedź JSON, konfiguracja żądania |
| unikalne, a ich kolejność nie ma znaczenia | set | role, uprawnienia, tagi, porównanie dwóch zestawów |
| uporządkowane, ale stałe po utworzeniu | tuple | stały zestaw wartości, para współrzędnych, stała kombinacja danych |
Najważniejsze rozróżnienie brzmi:
- lista i krotka są sekwencjami: mają indeksy, czyli pozycje
0,1,2i tak dalej; - słownik przechowuje pary
klucz: wartość; - zbiór przechowuje tylko unikalne elementy i nie służy do pracy po indeksach.
Na początek obejrzyj krótkie, przekrojowe wprowadzenie. Zwróć uwagę przede wszystkim na cechy struktur, nie na wszystkie wymienione metody.
Arrays: listy, tuple, sety, słowniki - Poradnik Python #3
Film „Arrays: listy, tuple, sety, słowniki - Poradnik Python #3” z kanału DooMx pokazuje cztery podstawowe kolekcje w jednym, krótkim przebiegu. Obejrzyj go, aby połączyć składnię nawiasów z właściwościami każdej struktury.
Zacznij od przeglądu kolekcji. Następnie obejrzyj listy, zwracając uwagę na indeksowanie i modyfikację. Przejdź przez krotki, a potem zbiory, aby utrwalić różnicę między niezmiennością a unikalnością. Zakończ fragmentami o slownikach i pustych kolekcjach.
Lista: gdy liczy się kolejność i możliwość zmiany
Listę zapisujesz w nawiasach kwadratowych:
produkt_ids = [101, 204, 204, 518]
Lista:
- zachowuje kolejność elementów;
- dopuszcza duplikaty;
- pozwala zmieniać, dodawać i usuwać elementy;
- pozwala pobierać elementy po indeksie.
produkty = ["klawiatura", "mysz"]
print(produkty[0]) # klawiatura
produkty.append("monitor")
produkty[1] = "mysz bezprzewodowa"
print(produkty)
# ['klawiatura', 'mysz bezprzewodowa', 'monitor']
Indeks 0 oznacza pierwszy element. To źródło częstych błędów przy początku nauki, ale szybko staje się naturalne.
W testach lista dobrze reprezentuje dane, których porządek należy zachować. Na przykład odpowiedź API może zwracać listę produktów w określonej kolejności, a test może sprawdzić pierwszy element:
products = [
{"id": 10, "name": "Klawiatura"},
{"id": 20, "name": "Mysz"},
]
assert products[0]["name"] == "Klawiatura"
Lista jest też odpowiednia, gdy element może pojawić się więcej niż raz. W zamówieniu klient może dodać dwie sztuki tego samego produktu — zbiór byłby wtedy błędnym wyborem, bo usunąłby powtórzenie.
W praktyce przydadzą się zwłaszcza metody:
statusy = ["nowe", "w trakcie"]
statusy.append("zakończone") # dodaje na końcu
statusy.remove("w trakcie") # usuwa pierwsze wystąpienie wartości
ostatni_status = statusy.pop() # usuwa i zwraca ostatni element
Metody takie jak append(), remove() i sort() zmieniają istniejącą listę. Z tego powodu nie przypisuj wyniku append() do zmiennej:
# Niepoprawnie:
# statusy = statusy.append("anulowane")
Po takim zapisie statusy otrzymałoby wartość None, bo append() modyfikuje listę na miejscu, zamiast tworzyć nową.
Krotka: dane o stałej strukturze
Krotkę zwykle zapisujesz w nawiasach okrągłych:
endpoint = ("POST", "/users")
Tak jak lista, krotka zachowuje kolejność i pozwala odczytywać elementy indeksami:
endpoint = ("POST", "/users")
print(endpoint[0]) # POST
print(endpoint[1]) # /users
Kluczowa różnica: krotki są niemodyfikowalne. Po utworzeniu nie można podmienić elementu, niczego dodać ani usunąć.
oczekiwana_odpowiedz = (201, "Utworzono użytkownika")
# oczekiwana_odpowiedz[0] = 200
# TypeError: 'tuple' object does not support item assignment
W testach krotka pasuje do niewielkich zestawów wartości, których rola wynika z pozycji i które nie powinny być przypadkowo zmieniane. Przykład: para złożona z kodu statusu i komunikatu.
expected_result = (201, "user created")
expected_status, expected_message = expected_result
print(expected_status) # 201
print(expected_message) # user created
Powyższy zapis to rozpakowywanie krotki. Dwie wartości z krotki zostają przypisane do dwóch nazw. Jest czytelniejszy niż ciągłe używanie indeksów 0 i 1.
Drobny, ale ważny detal składni: krotka jednoelementowa wymaga przecinka.
jedna_wartosc = ("admin",) # krotka
tekst = ("admin") # zwykły napis w nawiasach
Nie wybieraj krotki wyłącznie dlatego, że „jest podobna do listy”. Wybierz ją wtedy, gdy niezmienność rzeczywiście opisuje sens danych: np. ustalony zestaw wartości zwracanych przez funkcję albo stały identyfikator złożony z kilku części.
Zbiór: unikalność i porównywanie zestawów
Zbiór zapisujesz w nawiasach klamrowych:
role = {"admin", "editor", "viewer"}
Zbiór ma dwa kluczowe ograniczenia:
- ten sam element występuje najwyżej raz;
- nie należy polegać na kolejności elementów.
role = {"editor", "viewer", "editor"}
print(role)
# {'editor', 'viewer'} — kolejność wyświetlenia może być inna
Nie odczytasz elementu przez indeks:
# role[0]
# TypeError: 'set' object is not subscriptable
Zbiory są szczególnie użyteczne w automatyzacji, kiedy test ma sprawdzić, czy system zwrócił dokładnie taki zestaw uprawnień, tagów lub funkcji — niezależnie od ich kolejności.
expected_roles = {"editor", "viewer"}
actual_roles = {"viewer", "editor"}
assert actual_roles == expected_roles
Gdyby role były listami, porównanie zależałoby od kolejności:
["viewer", "editor"] == ["editor", "viewer"]
# False
W przypadku ról kolejność zazwyczaj nie ma znaczenia, więc set wyraża intencję testu lepiej niż list.
Operacje na zbiorach pomagają diagnozować różnicę między stanem oczekiwanym a rzeczywistym:
expected_permissions = {"read", "write", "delete"}
actual_permissions = {"read", "write"}
missing_permissions = expected_permissions - actual_permissions
unexpected_permissions = actual_permissions - expected_permissions
common_permissions = expected_permissions & actual_permissions
print(missing_permissions) # {'delete'}
print(unexpected_permissions) # set()
print(common_permissions) # {'read', 'write'}
Znaczenie operatorów:
| Zapis | Znaczenie w testach |
|---|---|
a & b | elementy wspólne dla obu zbiorów |
a | b | wszystkie elementy z obu zbiorów |
a - b | elementy w a, których nie ma w b |
a == b | oba zbiory mają dokładnie te same elementy |
Pusty zbiór tworzysz przez set(), a nie przez {}:
empty_set = set()
empty_dict = {}
Zapis {} tworzy pusty słownik. To jedna z niewielu sytuacji, w których składnia dwóch struktur wygląda inaczej.

Słownik: dane z nazwanymi polami
Słownik zapisujesz za pomocą par klucz: wartość:
user = {
"id": 42,
"email": "ania@example.test",
"active": True,
}
W automatyzacji testów słownik jest prawdopodobnie najczęściej spotykaną strukturą. Odpowiedzi API w formacie JSON, po odczytaniu przez Pythona, zwykle stają się słownikami zagnieżdżonymi z listami.
Wartości odczytujesz przez klucz:
user = {
"id": 42,
"email": "ania@example.test",
"active": True,
}
print(user["email"]) # ania@example.test
Dostęp przez nawiasy kwadratowe jest dobry, gdy brak pola oznacza błąd testu lub błąd kontraktu API:
assert user["id"] == 42
Jeśli pole może nie wystąpić, użyj get():
marketing_consent = user.get("marketing_consent", False)
Tutaj, jeśli klucza "marketing_consent" nie ma w słowniku, program zwróci False zamiast zgłaszać KeyError.
Słowniki są modyfikowalne:
user["role"] = "viewer"
user["active"] = False
Klucze w słowniku muszą być unikalne. Przypisanie wartości pod istniejącym kluczem zastępuje poprzednią wartość:
config = {"base_url": "https://test.example"}
config["base_url"] = "https://staging.example"
Istotna pułapka: operator in sprawdza w słowniku klucze, a nie wartości.
response = {
"status": "active",
"role": "editor",
}
print("role" in response) # True
print("editor" in response) # False
print("editor" in response.values()) # True
Choć współczesny Python zachowuje kolejność dodawania kluczy do słownika, nie to jest głównym powodem używania dict. Słownik wybieramy przede wszystkim dlatego, że dane mają nazwane znaczenie: "email", "password", "expected_status", "base_url".
Utrwal teraz składnię i podstawowe operacje w dokumentacji Pythona. Nie musisz poznawać wszystkich metod na pamięć; ważniejsze jest rozpoznawanie, kiedy dana struktura odzwierciedla sens danych.
Przeczytaj wybrane części oficjalnego „Tutoriala Pythona” w polskim tłumaczeniu. Materiał porządkuje metody list oraz różnice między krotkami, zbiorami i słownikami.
W sekcji 5.1 „Więcej na temat list” przeczytaj opis podstawowych metod od append() do copy(); przejdź przez metody list i odnieś je do zmienianej sekwencji danych testowych. Następnie przeczytaj całą sekcję 5.3 „Krotki i sekwencje”, szczególnie fragment o niezmienności i rozpakowywaniu; jako punkt orientacyjny użyj krotek i rozpakowania. W sekcji 5.4 „Zbiory” skup się na usuwaniu duplikatow i operatorach czesci wspolnej oraz roznicy, od operacji zbiorow. Na koniec przeczytaj sekcję 5.5 „Słowniki” od zdania opisującego słownik jako typ wbudowany do przykładu z konstruktorem dict(); zobacz podstawy slownikow. Zwróć szczególną uwagę na różnicę między d[key] i d.get(key).
Jeden realistyczny model danych testowych
W prawdziwym teście struktury często występują razem. Nie chodzi o to, by użyć wszystkich naraz, lecz by każda odpowiadała na inne wymaganie.
test_case = {
"name": "utworzenie aktywnego użytkownika",
"request_body": {
"email": "anna@example.test",
"password": "BezpieczneHaslo123",
},
"expected_status": 201,
"expected_roles": {"viewer", "newsletter"},
}
actual_response = {
"id": 101,
"email": "anna@example.test",
"roles": ["newsletter", "viewer"],
"active": True,
}
Przyjrzyjmy się decyzjom:
test_casejest słownikiem, bo pola mają jasne nazwy.request_bodyjest kolejnym słownikiem, bo naśladuje strukturę obiektu JSON.expected_rolesjest zbiorem, bo kolejność ról nie powinna wpływać na wynik testu.actual_response["roles"]jest listą, ponieważ odpowiedzi JSON reprezentują tablice jako listy Pythona.
Sprawdzenie ról może wyglądać tak:
assert actual_response["email"] == test_case["request_body"]["email"]
assert set(actual_response["roles"]) == test_case["expected_roles"]
assert actual_response["active"] is True
Konwersja set(actual_response["roles"]) oznacza: „w tym konkretnym sprawdzeniu nie interesuje mnie kolejność, lecz pełny zestaw unikalnych ról”.
Nie wysyłaj jednak zbioru bezpośrednio jako JSON. Standard JSON nie ma typu set; jeśli API oczekuje tablicy, do żądania stosuj listę:
request_body = {
"email": "anna@example.test",
"roles": ["viewer", "newsletter"],
}
Zbiór pozostaje wygodną strukturą wewnątrz testu, gdzie służy do porównań.
Gdy będziesz przechowywać wiele scenariuszy, naturalne będzie użycie listy słowników:
test_cases = [
{
"name": "poprawne dane",
"email": "anna@example.test",
"expected_status": 201,
},
{
"name": "brak adresu email",
"email": "",
"expected_status": 400,
},
]
Lista zachowuje kolejność scenariuszy i pozwala je stopniowo dodawać. Każdy scenariusz jest słownikiem, ponieważ jego wartości opisują nazwy pól, a nie pozycje.
Szybka reguła do zastosowania w kodzie
Przed zapisaniem danych wybierz strukturę według ich kontraktu:
# Lista: zmienna, uporządkowana sekwencja; duplikaty są dozwolone
cart_items = ["book", "book", "pen"]
# Krotka: stałe, uporządkowane wartości o określonych pozycjach
http_target = ("GET", "/users/42")
# Zbiór: unikalne wartości; kolejność nie ma znaczenia
expected_tags = {"regression", "api"}
# Słownik: pola opisane kluczami
credentials = {
"username": "tester",
"password": "secret",
}
Możesz zapamiętać to w jednym zdaniu:
Lista opisuje przebieg, słownik opisuje obiekt, zbiór opisuje unikalny zestaw, a krotka opisuje stały układ wartości.
Najważniejsze wnioski z lekcji:
- Listy są uporządkowane, modyfikowalne i dopuszczają duplikaty.
- Krotki są uporządkowane, ale niemodyfikowalne; pasują do stałych zestawów wartości.
- Zbiory przechowują unikalne elementy bez gwarantowanej kolejności; sprawdzają się przy rolach, tagach i uprawnieniach.
- Słowniki mapują klucze na wartości; są podstawową strukturą dla danych użytkownika, konfiguracji i obiektów JSON.
- W słowniku
insprawdza klucze, a nie wartości. - W testach często łączysz struktury, np. listę scenariuszy, słowniki danych i zbiory oczekiwanych uprawnień.
W następnej lekcji przejdziemy do funkcji: zdefiniujesz parametry pozycyjne, nazwane i domyślne oraz nauczysz się zwracać wartości. Dzięki temu zaczniesz wyprowadzać powtarzalną logikę poza pojedyncze testy.
Can't find a good explanation? Sign up and we'll make it for you
Sign up