Niestabilne testy: Co je powoduje i jak je naprawić
Test, który losowo kończy się niepowodzeniem, uczy Twój zespół ignorowania niepowodzeń. Dowiedz się, jak potwierdzić, że test działa nieprawidłowo, znaleźć prawdziwą przyczynę i naprawić ją, korzystając z wersji demonstracyjnych pokazujących, co faktycznie działa.
- Niepewny test kończy się powodzeniem i niepowodzeniem w przypadku tego samego kodu, bez zmian.
- Typowymi przyczynami są czas, stan współdzielenia, niestabilne dane, usługi zewnętrzne i różnice w środowisku.
- Zastąp stałe stany uśpienia oczekiwaniem na określony stan i nadaj każdemu testowi własne dane.
- Ponowne próby mogą ukryć prawdziwe błędy. Poddaj niestabilny test kwarantannie, napraw go w określonym terminie lub usuń.
Kompilacja staje się czerwona, ktoś klika „Uruchom ponownie”, przechodzi i wszyscy idą dalej. Wydaje się to nieszkodliwe, ale niestabilne testy powoli niszczą najcenniejszą rzecz, jaką ma zestaw testów: zaufanie ludzi do niego. Kiedy awarie są zwykle hałasem, pojawiają się także prawdziwe błędy.
W tym przewodniku opisano, dlaczego testy działają nieprawidłowo i jak prawidłowo naprawić każdą przyczynę. W Pythonie 3.13 uruchomiliśmy demonstracje synchronizacji i stanu współdzielonego, a pokazane liczby pochodzą z tych przebiegów. Są to małe demonstracje zaprojektowane tak, aby łatwo było dostrzec łuszczenie się, więc Twoje własne wskaźniki awaryjności będą się różnić.
Co to jest test niestabilny?
Niestabilny test daje różne wyniki w tym samym kodzie. W jednym przebiegu przechodzi pomyślnie, a w kolejnym kończy się niepowodzeniem, mimo że w produkcie nic się nie zmieniło. Test może być wadliwy lub kod może zawierać naprawdę sporadyczny błąd. Ta druga możliwość jest właśnie powodem, dla którego nie powinieneś ich po prostu odrzucać.
Dlaczego niestabilne testy są tak kosztowne
- Utracone zaufanie. Kiedy ludzie założą się, że kolor czerwony oznacza „prawdopodobnie niestabilny”, przestają badać prawdziwe awarie.
- Zmarnowany czas. Programiści spędzają godziny na ponownym uruchamianiu potoków i badaniu błędów, które nie mają nic wspólnego z ich zmianami.
- Wolniejsze dostarczanie. Łączenia i wydania czekają na powtórki, podważając sens CI/CD.
- Ukryte błędy. Warunki wyścigowe w produkcie mogą wyglądać jak hałas testowy i to właśnie one szkodzą w produkcji.
Najczęstsze przyczyny w skrócie
| Przyczyna | Typowy znak | Zwykła poprawka |
|---|---|---|
| Czas i asynchronia | Zawodzi częściej, gdy CI jest zajęty | Poczekaj na warunek |
| Stan wspólny | Niepowodzenie w zależności od kolejności testu | Izoluj dane, resetuj stan |
| Daty i losowość | Zawodzi w określonych godzinach lub dniach | Kontroluj zegar i nasiona |
| Kruche selektory | Niepowodzenie po zmianach interfejsu użytkownika | Używaj stabilnych lokalizatorów |
| Usługi zewnętrzne | Nie powiedzie się, gdy zależność jest powolna | Wyśmiewaj je lub karć |
| Różnice środowiskowe | Zaliczony lokalnie, niepowodzenie w CI | Wyrównaj z pojemnikami |
| Limity zasobów | Nie powiedzie się w przypadku przebiegów równoległych | Dostosuj pracowników, napraw spór |
Przyczyna 1: Czas i stałe godziny snu
Jest to najczęstsza przyczyna. Test zakłada, że praca w tle zostanie zakończona po ustalonym opóźnieniu. Aby zobaczyć efekt napisaliśmy mały test, w którym praca trwa do 2 ms, natomiast test czeka tylko 1 ms przed sprawdzeniem:
def flaky_timing():
result = {}
def work():
time.sleep(random.uniform(0, 0.002))
result["done"] = True
threading.Thread(target=work).start()
time.sleep(0.001) # hope it has finished
assert result.get("done")
Rozwiązaniem jest poczekanie na rzeczywisty stan, z dużym górnym limitem:
def fixed_timing():
result = threading.Event()
def work():
time.sleep(random.uniform(0, 0.002))
result.set()
threading.Thread(target=work).start()
assert result.wait(timeout=1) # continues as soon as it is ready
Każdą wersję uruchomiliśmy 200 razy:
| Wersja | Awarie na 200 |
|---|---|
| Stały sen | 102 |
| Poczekaj na warunek | 0 |
Rezultatem jest coś więcej niż tylko niezawodność. Wersja naprawiona działa dalej w momencie zakończenia pracy, więc zwykle jest też szybsza. Dłuższy tryb uśpienia sprawi, że pierwsza wersja zawiedzie rzadziej, ale każde uruchomienie będzie wówczas wolniejsze i nadal może zakończyć się niepowodzeniem na obciążonej maszynie CI.
W testach przeglądarki obowiązuje ta sama zasada. Użyj automatycznego oczekiwania Playwright lub jawnego Selenium czeka zamiast time.sleep:
# Playwright: retries until true or a timeout is reached
expect(page.get_by_text("Order placed")).to_be_visible()
# Selenium: an explicit wait for a condition
WebDriverWait(driver, 10).until(
EC.visibility_of_element_located((By.ID, "confirmation"))
)
Przyczyna 2: Stan udostępniony i kolejność testów
Kiedy testy udostępniają dane, jeden może wpływać na inny, a wynik zależy od kolejności, w jakiej są uruchamiane. Te dwa testy korzystają z jednej wspólnej listy:
store = []
def test_add():
store.append("x")
assert len(store) == 1
def test_empty():
assert store == []
| Zamów | test_add | test_empty |
|---|---|---|
| pusty, a następnie dodaj | Przełęcz | Przełęcz |
| dodaj, a następnie opróżnij | Przełęcz | Niepowodzenie |
Nasz test to potwierdził i dlatego pakiet może działać miesiącami, a potem się zepsuć, gdy ktoś doda równoległe uruchomienia lub losową wtyczkę. Lekarstwem jest izolacja:
- Daj każdemu testowi świeże dane, używając urządzenia, które je konfiguruje i czyści. Nasz samouczek dotyczący testów Pythona pokazuje, jak to zrobić.
- Twórz unikalne rekordy, takie jak e-mail z losowym sufiksem, zamiast udostępnionego „użytkownika testowego”.
- Zawiń testy bazy danych w transakcję i wycofaj je, jeśli pozwala na to konfiguracja.
- Regularnie przeprowadzaj testy w losowej kolejności, aby problemy te pojawiały się wcześnie.
Przyczyna 3: Daty, strefy czasowe i losowość
Test, który zaliczony jest przez cały tydzień, może zakończyć się niepowodzeniem w sobotę, o północy lub ostatniego dnia miesiąca. Ustaw czas jako dane wejściowe, zamiast czytać zegar w swojej logice:
def is_weekend(today=None):
today = today or datetime.date.today()
return today.weekday() >= 5
is_weekend(datetime.date(2026, 11, 7)) # True (a Saturday)
is_weekend(datetime.date(2026, 11, 9)) # False (a Monday)
Testy te dają tę samą odpowiedź każdego dnia. Podobnie, ustaw stałe ziarno dla wartości losowych w testach i zarejestruj je, gdy użyjesz danych losowych, aby móc odtworzyć awarię. Nie zakładaj określonej kolejności ze zbiorów, słowników ze źródeł zewnętrznych lub zapytań do baz danych z numerem ORDER BY.
Przyczyna 4: Kruche selektory
Test, który znajdzie przycisk z div:nth-child(3) > span, kończy się niepowodzeniem przy jakichkolwiek zmianach układu i może zakończyć się niepowodzeniem tylko w przypadku niektórych rozmiarów lub wariantów ekranu. Preferuj lokalizatory oparte na tym, co widzą użytkownicy, lub dedykowane atrybuty testowe. W naszym przewodniku po lokatorach dramaturgów wyjaśniono, w jakiej kolejności je wypróbowywać. Animacje są kolejnym częstym źródłem problemów, więc rozważ wyłączenie ich w środowiskach testowych.
Przyczyna 5: Usługi i sieci zewnętrzne
Jeśli test wywołuje prawdziwego dostawcę płatności lub interfejs API strony trzeciej, jego wynik zależy od limitów czasu pracy i stawek innej osoby. W przypadku większości testów kpij z tych wywołań i prowadź niewielką liczbę oddzielnych kontroli kontraktów w porównaniu z rzeczywistą usługą, abyś nadal wiedział, czy się zmieni. W naszym przewodniku po testowaniu jednostkowym i integracyjnym wyjaśniamy, jak je podzielić.
Wskazówka: „Zalicza się lokalnie, kończy się niepowodzeniem w CI” zwykle oznacza różnicę w szybkości, strefie czasowej, zasobach lub wersjach. Uruchamianie testów w tym samym obrazie kontenera lokalnie i w CI usuwa wiele z nich. Nasz samouczek Docker Compose może okazać się pomocny.
Jak zdiagnozować niestabilny test
- Potwierdź. Uruchom test wiele razy bez zmian. Z pytestem działa prosta pętla powłoki, a wtyczka taka jak
pytest-repeatdaje--count=100. - Zapisz dowody. Zapisuj dzienniki, zrzuty ekranu i ślady każdej awarii. Szczególnie przydatna jest tutaj przeglądarka śladów Playwrighta.
- Podkreśl warunki. Uruchom go z ograniczonym procesorem, równolegle lub w kolejności losowej. Częstsze niepowodzenia ułatwiają naukę.
- Przetestuj sam. Jeśli zawsze przechodzi pomyślnie samodzielnie, ale kończy się niepowodzeniem w pakiecie, podejrzewaj stan współdzielony.
- Poszukaj wzorców. Sprawdź, które maszyny, pory dnia i sąsiednie testy są zaangażowane.
- Sprawdź produkt. Zapytaj szczerze, czy w Twojej aplikacji może to być prawdziwy stan wyścigowy.
Czy automatyczne ponowne próby są rozwiązaniem?
Ponowne próby stanowią użyteczną krótkoterminową siatkę bezpieczeństwa i nie stanowią lekarstwa. Pojedyncza automatyczna ponowna próba w CI może kontynuować pracę podczas badania, ale test, który wymaga ponownej próby, aby przejść pomyślnie, nadal coś ci mówi. Co gorsza, prawdziwy, sporadyczny błąd produktu, który kończy się niepowodzeniem w 20% przypadków, zwykle zostaje niezauważony podczas ponownej próby.
Stosuj ponawianie prób zgodnie z poniższymi zasadami: ogranicz je do jednej lub dwóch, zapisuj każdy test, który wymagał wykonania jednego testu, i traktuj tę listę jako listę rzeczy do zrobienia. Test zgłoszony jako „zaliczony przy ponownej próbie” powinien być traktowany jako problem do zbadania, a nie jako sukces.
Proces zespołowy do niestabilnych testów
- Zmierz. Śledź, które testy zakończyły się niepowodzeniem, a następnie przekaż to samo zatwierdzenie. Może to pokazać wiele narzędzi CI i reporterów testów.
- Kwarantanna. Przenieś potwierdzony niestabilny test z serii blokującej, aby przestał przeszkadzać wszystkim, ale wykonuj go dalej i śledź.
- Wyznacz właściciela i termin. Kwarantanna bez terminu staje się po prostu cmentarzem.
- Napraw lub usuń. Jeśli testu nie warto naprawiać, usuń go i sprawdź, czy nadal istnieje gdzie indziej.
- Zapobiegaj. Przed połączeniem przeprowadź kilka nowych testów. Test, który kończy się niepowodzeniem raz na pięćdziesiąt serii, jest lepiej wykryty teraz niż za sześć miesięcy.
Traktuj niestabilny test jako błąd z priorytetem, a nie jako szum tła. Ważność i priorytet mają tu również zastosowanie, zgodnie z opisem w ważność a priorytet. Jeśli niestabilność jest powszechna, przeczytaj o szerszych błędach automatyzacji testów i zastanów się, czy nie masz zbyt wielu testów na poziomie przeglądarki.
Podsumowanie
Niestabilne testy wydają się przypadkowe, ale rzadko takie są. Prawie każdy sprowadza się do założenia dotyczącego czasu, porządku, danych lub środowiska, które nie zawsze jest prawdziwe. Wybierz w tym tygodniu test, który najczęściej kończy się niepowodzeniem. Uruchom go pięćdziesiąt razy, znajdź założenie i napraw to na dobre. Jedna prawdziwa poprawka uczy zespół więcej niż sto powtórek.
Najczęstsze pytania
Co to jest test niestabilny?
Niestabilny test przechodzi pomyślnie w niektórych uruchomieniach, a w innych kończy się niepowodzeniem bez żadnych zmian w kodzie. Przyczyną może być test, środowisko lub produkt.
Co powoduje niestabilne testy?
Najczęstszymi przyczynami są założenia dotyczące czasu, wspólny stan między testami, niestabilne selektory, usługi zewnętrzne, obsługa dat i różnice między CI a maszynami lokalnymi.
Czy powinienem usunąć niestabilne testy?
Najpierw spróbuj je naprawić. Jeśli test ma niską wartość i jest kosztowny w naprawie, usunięcie go jest w porządku, ale upewnij się, że jego ważne informacje znajdują się gdzie indziej.
Czy powinienem automatycznie ponawiać nieudane testy?
Jako ograniczone zabezpieczenie tak, ale zapisuj każdą ponowną próbę i sprawdź ją. Same ponowne próby ukrywają problemy i mogą pozwolić, aby rzeczywiste, sporadyczne błędy dotarły do środowiska produkcyjnego.
Jak znaleźć niestabilne testy?
Poszukaj testów, które zakończyły się niepowodzeniem i przeszły to samo zatwierdzenie, korzystając z historii CI lub narzędzia do raportowania. Możesz także uruchamiać testy podejrzanych wiele razy w pętli.
Ucz się dalej
Potrzebujesz pomocy przy testowaniu, WordPressie lub SEO?
Pomagamy zespołom dostarczać lepsze oprogramowanie i rozwijać ich witryny. Powiedz nam, nad czym pracujesz, a my zaproponujemy jasny następny krok.
Więcej poradników na testowanie, sztuczna inteligencja i nowoczesny rozwój
Podobał Ci się ten artykuł? Wybierz temat i przejdź bezpośrednio do naszych najnowszych tutoriali.
Wyjaśnienie halucynacji AI: dlaczego modele językowe tworzą rzeczy i jak je zmniejszyć
Wyjaśniono halucynacje AI: czym są, dlaczego powstają w modelach językowych, typowe typy, jak je zredukować za pomocą uziemień i podpowiedzi oraz pr…
Przeczytaj artykuł →DevOpsWyjaśnienie zmiennych środowiskowych: pliki .env, sekrety i konfiguracja wykonana prawidłowo
Zmienne środowiskowe wyjaśnione na przykładach Pythona, które przeprowadziliśmy: ich odczytywanie i ustawianie, dlaczego każda wartość jest ciągiem …
Przeczytaj artykuł →Tworzenie oprogramowaniaWyjaśnienie ograniczenia szybkości interfejsu API: algorytmy, nagłówki i obsługa błędów 429
Ograniczanie szybkości API wyjaśnione za pomocą przeprowadzonej przez nas symulacji: zasobnik tokenów, stałe okno, przesuwane okno, 429 odpowiedzi, …
Przeczytaj artykuł →Automatyzacja testówWyśmiewanie w Pythonie: unittest.mock, patch i błędy, które sprawiają, że testy kłamią
Wyśmiewanie w Pythonie za pomocą unittest.mock, przetestowane: Mock i zwracana wartość, efekt uboczny, łatanie tam, gdzie używana jest nazwa, specyf…
Przeczytaj artykuł →Testowanie oprogramowaniaWyjaśnienie zasięgu kodu: pokrycie linii i oddziału oraz co naprawdę oznacza 100%
Wyjaśnienie pokrycia kodu za pomocą prawdziwego pliku cover.py: pokrycie linii, gałęzi i warunków, przeczytanie raportu, dlaczego 100% pokrycia może…
Przeczytaj artykuł →Bazy danych i SQLDELETE vs TRUNCATE vs DROP w SQL: różnice, wycofywanie i kiedy używać każdego z nich
DELETE vs TRUNCATE vs DROP wyjaśnione za pomocą przetestowanego SQL: co każdy usuwa, wycofywanie, wyzwalacze, resetowanie tożsamości, prędkość i czy…
Przeczytaj artykuł →Bazy danych i SQLWyjaśnienie widoków SQL: tworzenie, używanie i unikanie pułapek wraz z przykładami
Widoki SQL wyjaśnione za pomocą zapytań, które uruchomiliśmy: tworzenie widoku, wyniki na żywo, ukrywanie kolumn, wydajność, widoki z możliwością ak…
Przeczytaj artykuł →Tworzenie oprogramowaniaInterfejs TypeScript a typ: rzeczywiste różnice, testowane za pomocą kompilatora
Interfejs TypeScript a typ, sprawdzony z prawdziwym kompilatorem: co oba mogą zrobić, co tylko jeden, łączenie deklaracji, rozszerzenia a przecięcia…
Przeczytaj artykuł →Tworzenie oprogramowaniaPython *args i **kwargs wyjaśnione: elastyczne funkcje z przejrzystymi przykładami
Python *args i **kwargs wyjaśnione za pomocą uruchomionego przez nas kodu: pakowanie i rozpakowywanie, kolejność parametrów, argumenty zawierające t…
Przeczytaj artykuł →Tworzenie oprogramowaniaFunkcje Lambda w Pythonie: składnia, rzeczywiste zastosowania i kiedy ich nie używać
Funkcje lambda w Pythonie wyjaśnione za pomocą uruchomionego przez nas kodu: składnia, sortowanie za pomocą klucza, mapa i filtr, domknięcia, pułapk…
Przeczytaj artykuł →Sztuczna inteligencjaKim są agenci AI? Jak one działają, dzięki prostej pętli agenta, którą możesz uruchomić
Kim są agenci AI? Prosty przewodnik po pętli agenta, narzędziach, pamięci i poręczach, z małą pętlą w Pythonie, którą uruchomiliśmy, prawdziwymi zag…
Przeczytaj artykuł →DevOpsMonitorowanie a obserwowalność: prawdziwa różnica z logami, wskaźnikami i śladami
Wyjaśnienie monitorowania a obserwowalności: znane i nieznane awarie, dzienniki, metryki i ślady, opis incydentu przy kasie, złote sygnały, OpenTele…
Przeczytaj artykuł →DevOpsWoluminy platformy Docker a mocowania powiązań: utrwalanie danych kontenera we właściwy sposób
Wyjaśniono woluminy Dockera a montowanie powiązań: dlaczego dane kontenera znikają, nazwane woluminy, montowanie powiązań, tmpfs, przykłady tworzeni…
Przeczytaj artykuł →Automatyzacja testówTDD vs BDD: kluczowe różnice, jedna funkcja zbudowana w obie strony i kiedy używać każdej z nich
TDD vs BDD wyjaśnione za pomocą jednej funkcji rabatowej zbudowanej w obie strony: refaktoryzacja czerwono-zielona z uruchomionym przez nas kodem, s…
Przeczytaj artykuł →Testowanie oprogramowaniaTestowanie akceptacyjne użytkownika (UAT): proces, lista kontrolna i rzeczywiste przykłady
Wyjaśniono testy akceptacyjne użytkownika: kto je przeprowadza, czym różni się od kontroli jakości, siedmioetapowy proces, szablon przypadku testowe…
Przeczytaj artykuł →Bazy danych i SQLWyjaśnienie wartości SQL NULL: IS NULL, COALESCE, NULLIF i pułapki, których należy unikać
Wartości SQL NULL wyjaśnione za pomocą zapytań, które wykonaliśmy: logika trójwartościowa, IS NULL, COALESCE, NULLIF, w jaki sposób agregacje, łącze…
Przeczytaj artykuł →Bazy danych i SQLInstrukcja SQL CASE WHEN: składnia, rzeczywiste przykłady i typowe błędy
SQL CASE WHEN wyjaśnione za pomocą zapytań, które uruchomiliśmy: proste i przeszukiwane formularze, kolejność warunków, elementy przestawne z SUM, s…
Przeczytaj artykuł →Tworzenie oprogramowaniaCo to jest JWT? Tokeny internetowe JSON wyjaśnione na przykładzie działania
Co to jest JWT? Wyjaśniono tokeny sieciowe JSON: trzy części, token zbudowany i zmodyfikowany w uruchomionym przez nas kodzie, JWT a sesje, pamięć m…
Przeczytaj artykuł →Tworzenie oprogramowaniaWyjaśnienie pętli zdarzeń JavaScript: stos wywołań, mikrozadania i liczniki czasu z przykładami
Pętla zdarzeń JavaScript wyjaśniona za pomocą uruchomionego przez nas kodu: stos wywołań, kolejki zadań i mikrozadań, dlaczego setTimeout 0 nie jest…
Przeczytaj artykuł →Tworzenie oprogramowaniaWyjaśnienie generatorów Pythona i wydajności: oszczędzaj pamięć dzięki prawdziwym przykładom
Generatory Pythona i wydajność wyjaśnione za pomocą uruchomionego przez nas kodu: sposób wstrzymywania i wznawiania, mierzone oszczędności pamięci, …
Przeczytaj artykuł →Sztuczna inteligencjaCzym są osady? Wyjaśnienie wektorów i podobieństwa cosinusa
Czym są osadzania? Prosty przewodnik po wektorach, podobieństwie cosinusów i wyszukiwaniu semantycznym, z małym przykładem, który przeprowadziliśmy,…
Przeczytaj artykuł →DevOpsWdrożenie niebiesko-zielone a kanaryjskie: różnice, kompromisy i sposób wyboru
Wyjaśniono wdrożenie Blue-Green vs Canary: sposób działania każdego z nich, koszty, ryzyko i wycofywanie zmian, przetestowane przez nas stabilne łąc…
Przeczytaj artykuł →Automatyzacja testówNiestabilne testy: co je powoduje i jak je naprawić na dobre
Niestabilne testy wyjaśnione w przeprowadzonych przez nas demonstracjach: główne przyczyny, sposób potwierdzenia i zdiagnozowania jednej z nich, odp…
Przeczytaj artykuł →Testowanie oprogramowaniaLoad Testing vs Stress Testing: Differences, Metrics and k6 Examples
Wyjaśniono testy obciążenia i testy warunków skrajnych: co dowodzi każdy z nich, testy szczytowe i nasiąkania, istotne metryki, dlaczego średnie wpr…
Przeczytaj artykuł →Testowanie oprogramowaniaPrzypadek testowy a scenariusz testowy: kluczowe różnice, przykłady i kiedy stosować każdy z nich
Wyjaśnienie przypadków testowych i scenariuszy testowych: jasne definicje, tabela obok siebie, jedna funkcja resetowania hasła zaczerpnięta ze scena…
Przeczytaj artykuł →Bazy danych i SQLOptymalizacja zapytań SQL: czytaj plany wykonania i napraw powolne zapytania z synchronizacją
Optymalizacja zapytań SQL przetestowana na milionie wierszy: przeczytaj plany wykonania, dodaj odpowiednie indeksy, napraw filtry niesargowalne, uży…
Przeczytaj artykuł →Bazy danych i SQLSQL UNION vs UNION ALL: różnice, przykłady i testowana wydajność
SQL UNION vs UNION ALL wyjaśnione na podstawie zapytań, które uruchomiliśmy: duplikaty, obie reguły są zgodne, prawdziwy błąd danych, zmierzona wyda…
Przeczytaj artykuł →Tworzenie oprogramowaniaSamouczek API pobierania JavaScript: GET, POST, obsługa błędów i limity czasu
Samouczek JavaScript Fetch API testowany na serwerze lokalnym: GET i POST, dlaczego 404 nie rzuca, pomocnik wielokrotnego użytku, ciągi zapytań, prz…
Przeczytaj artykuł →Tworzenie oprogramowaniaObsługa plików w Pythonie: odczyt, zapis, CSV, JSON i pathlib z przykładami
Obsługa plików w Pythonie wyjaśniona za pomocą uruchomionego przez nas kodu: tryby otwarte, czytanie dużych plików, CSV, JSON, pathlib, pliki binarn…
Przeczytaj artykuł →Tworzenie oprogramowaniaGit merge vs rebase: różnice, przykłady i kiedy czego używać
Git merge vs rebase wyjaśnione na prawdziwym wyniku logu, z tabelą porównawczą, złotą zasadą rebase i bezpiecznym procesem pracy zespołu.
Przeczytaj artykuł →Tworzenie oprogramowaniaPython list comprehension: składnia, przykłady i kiedy jej unikać
Python list comprehension wyjaśnione: składnia, filtrowanie, if-else, zagnieżdżone pętle, zmierzone czasy i sytuacje, w których zwykła pętla for jes…
Przeczytaj artykuł →Bazy danych i SQLSQL JOIN – wyjaśnienie z przykładami (Inner, Left, Right, Full)
Zrozum złączenia SQL krok po kroku na przykładowych tabelach i zapytaniach: INNER, LEFT, RIGHT, FULL, CROSS i SELF, plus typowe błędy i wskazówki wy…
Przeczytaj artykuł →