W automatyzacji testów wyrażenia regularne pomagają sprawdzać dynamiczne komunikaty, identyfikatory i format danych bez kruchych asercji. Pokazuję tu konkretne wzorce, sposób ich budowania oraz miejsca, w których regex naprawdę ułatwia życie, a nie tylko komplikuje test. To ważne zwłaszcza wtedy, gdy interfejs zmienia się często, a testy mają pozostać czytelne i odporne na drobne różnice w treści.
Najważniejsze rzeczy, które warto zapamiętać o regexach w testach
- Regex sprawdza się najlepiej tam, gdzie tekst jest częściowo dynamiczny, na przykład z numerem zamówienia, datą albo statusem.
- Dobry wzorzec zaczyna się od ograniczeń, a nie od szerokiego
.*. - W Playwright i Cypress można używać regexów zarówno w locatorach, jak i w asercjach.
- Zbyt ogólny wzorzec daje fałszywe pozytywy, więc test nadal przechodzi, choć produkt działa źle.
- W języku polskim i w testach wielojęzycznych trzeba uważać na Unicode, wielkość liter i semantykę
\w.
Gdzie regex naprawdę pomaga w automatyzacji testów
Ja traktuję wyrażenia regularne jako narzędzie do stabilnego dopasowania tekstu, a nie jako zamiennik wszystkich asercji. W testach przydają się szczególnie wtedy, gdy sprawdzany fragment ma stały szkielet, ale pojedyncze elementy zmieniają się między uruchomieniami: numer zamówienia, czas, kwota, identyfikator sesji, status procesu czy część komunikatu generowana przez backend.
Najczęściej regex rozwiązuje cztery konkretne problemy. Po pierwsze, pozwala kontrolować format, a nie jeden sztywny ciąg znaków, więc test nie psuje się po zmianie cyfry w numerze referencyjnym. Po drugie, pomaga weryfikować warianty treści, gdy produkt zwraca kilka akceptowalnych komunikatów. Po trzecie, ułatwia pracę z logami i raportami, gdzie trzeba znaleźć linię pasującą do wzorca. Po czwarte, bywa wygodny przy selektorach tekstowych, gdy element ma przewidywalny, ale nie w pełni stały opis.
Nie używam regexu wszędzie. Jeśli tekst jest stały, lepsza będzie zwykła, dokładna asercja. Regex wygrywa wtedy, gdy potrzebujesz kontroli nad strukturą danych, a nie nad każdym znakiem. Z tego powodu dobrze jest najpierw zrozumieć sam wzorzec, zanim zacznie się nim zastępować zbyt wiele prostych sprawdzeń.
Właśnie dlatego następny krok to zbudowanie wzorca tak, żeby był czytelny i odporny na przypadkowe dopasowania.
Jak budować wzorzec, żeby test był stabilny
W praktyce zaczynam od dwóch przykładów: jednego poprawnego i jednego błędnego. To wystarcza, żeby zobaczyć, czy wzorzec nie jest za szeroki. Dla testów automatycznych traktuję to jako minimum, ale przy bardziej złożonych przypadkach wolę sprawdzić nawet 3 poprawne i 3 błędne próbki. Dzięki temu szybciej wychwytuję niechciane dopasowania.
- Najpierw zapisuję stałą część tekstu, na przykład etykietę komunikatu.
- Potem oznaczam część zmienną, zwykle cyframi, słowem albo datą.
- Dopiero na końcu dodaję kotwice
^i$, żeby ograniczyć dopasowanie do całego tekstu. - Jeśli potrzebuję kilku dopuszczalnych wersji, używam alternatywy
|albo grupy nieprzechwytującej(?:...). - Na końcu sprawdzam flagi, szczególnie
idla wielkości liter iu, gdy pojawia się Unicode.
Przykład, który dobrze pokazuje taki tok myślenia, to numer zamówienia w UI:
^Zamówienie\s+#?\d{6}$Ten wzorzec mówi dokładnie tyle, ile trzeba: tekst zaczyna się od słowa „Zamówienie”, potem może pojawić się znak #, a dalej ma być sześć cyfr i nic więcej. To lepsze niż luźne dopasowanie typu Zamówienie.*, bo takie wyrażenie przepuściłoby też błędny komunikat z dodatkowymi słowami albo niepożądanym sufiksem.
Drugim dobrym nawykiem jest testowanie regexu w kontekście silnika, którego używa dany framework. W JavaScript nie warto zakładać, że wzorzec zachowa się tak samo jak w Pythonie czy w narzędziu zbudowanym na PCRE. To drobny szczegół, ale właśnie on najczęściej robi różnicę między testem przewidywalnym a testem „działającym tylko na papierze”.
Gdy wzorzec jest już uporządkowany, można przejść do konkretnych przykładów, które w automatyzacji pojawiają się najczęściej.
Przykłady, które najczęściej wracają w automatyzacji
Poniżej pokazuję wzorce, które w testach spotykam najczęściej. Każdy z nich rozwiązuje trochę inny problem, ale wszystkie mają wspólną cechę: sprawdzają strukturę, a nie przypadkowy ciąg znaków.
| Sytuacja testowa | Przykładowy wzorzec | Po co go używam | Na co uważam |
|---|---|---|---|
| Numer zamówienia w panelu | ^Zamówienie\s+#?\d{6}$ |
Weryfikuje stały opis i dokładnie sześć cyfr. | Nie dopuść dodatkowego tekstu przed ani po numerze. |
| Data w formacie ISO | ^\d{4}-\d{2}-\d{2}$ |
Sprawdza, czy data ma poprawny układ roku, miesiąca i dnia. | To walidacja formatu, nie poprawności kalendarzowej. |
| Kwota z groszami | ^\d+(?:,\d{2})?\s*zł$ |
Obsługuje wartości całkowite i ceny z dwoma miejscami po przecinku. | Ustal wcześniej, czy aplikacja używa przecinka czy kropki. |
| Komunikat sukcesu | ^(?:Zapisano|Utworzono|Zaktualizowano)$ |
Dopuszcza kilka równoważnych odpowiedzi z backendu albo UI. | Zbyt długi łańcuch alternatyw szybko staje się nieczytelny. |
| Dynamiczny identyfikator | ^user-\d{6}$ |
Pomaga sprawdzać techniczne identyfikatory generowane w locie. | Nie używaj tego zamiast dobrego selektora elementu. |
| Nazwa pliku z logiem | ^audit-\d{4}-\d{2}-\d{2}\.log$ |
Potwierdza poprawny schemat nazwy pliku w raporcie lub eksporcie. | Kropkę przed rozszerzeniem trzeba uciec jako \.. |
Najbardziej lubię przykład z komunikatem sukcesu, bo pokazuje sens regexu bez przesady. Jeśli produkt może powiedzieć „Zapisano”, „Utworzono” albo „Zaktualizowano”, to nie ma sensu upierać się przy jednym słowie, o ile z biznesowego punktu widzenia wszystkie trzy znaczą to samo. Z kolei data i kwota są dobrym przypomnieniem, że regex świetnie sprawdza format, ale nie zastępuje walidacji reguł domenowych.
W testach formularzy często pojawia się jeszcze adres e-mail. Tu też warto zachować umiar: prosty wzorzec wystarczy do sprawdzenia, czy użytkownik wpisał coś wyglądającego jak e-mail, ale nie udowodni, że skrzynka istnieje albo że wiadomość rzeczywiście dotrze. To dobra granica między praktycznym sprawdzeniem a nadmierną ambicją.
Skoro mamy już konkretne przykłady, dobrze jest zobaczyć, jak używać ich w narzędziach testowych bez tworzenia flaków i przypadkowych dopasowań.
Jak używać regexów w Playwright i Cypress bez kruchych testów
W Playwright regexy są wygodne, bo można ich używać zarówno w locatorach, jak i w asercjach. W praktyce daje to dobrą równowagę: locator znajduje element, a asercja sprawdza jego treść z automatycznym ponawianiem. Cypress też dobrze radzi sobie z dopasowaniem wzorców, ale zwykle używam go bardziej świadomie, ponieważ częściej trzeba tam samemu zadbać o to, co dokładnie jest subjectem asercji.
| Narzędzie | Gdzie regex jest najpraktyczniejszy | Co daje w testach |
|---|---|---|
| Playwright |
getByText(), getByLabel(), hasText, toHaveText()
|
Wygodne dopasowanie tekstu z auto-retry i czytelną składnią. |
| Cypress |
contains(), invoke('text') z should('match')
|
Elastyczne sprawdzanie treści widocznej w DOM, szczególnie przy dynamicznym copy. |
| Inne narzędzia webowe | Selektory tekstowe i asercje na stringach | Tę samą logikę da się zastosować wszędzie tam, gdzie test porównuje tekst. |
Przykład w Playwright:
await expect(page.getByText(/zamówienie #\d+/i)).toBeVisible();
await expect(page.getByTestId('status')).toHaveText(/^(zapisano|utworzono|zaktualizowano)$/i);Przykład w Cypress:
cy.contains(/zamówienie #\d+/i).should('be.visible');
cy.get('[data-testid="status"]')
.invoke('text')
.should('match', /^(Zapisano|Utworzono|Zaktualizowano)$/i);W Playwright szczególnie cenię to, że asercje typu toHaveText() potrafią czekać na właściwy stan elementu. To redukuje testowy szum, ale nie zwalnia z myślenia: jeśli regex jest zbyt szeroki, test nadal może przejść z błędnym komunikatem. Dlatego zawsze łączę wygodę auto-retry z precyzyjnym wzorcem, a nie z luźnym dopasowaniem „na oko”.
Na tym etapie najważniejsze staje się już nie to, jak regex wpisać, ale jak uniknąć błędów, które pozornie wszystko upraszczają, a w praktyce psują stabilność testów.
Najczęstsze błędy, które psują dopasowanie
W testach automatycznych najwięcej problemów widzę nie w samej składni, tylko w złym założeniu, co ma być sprawdzane. Regex potrafi ukryć błąd, jeśli jest zbyt szeroki, albo wywołać fałszywy alarm, jeśli jest zbyt ciasny. To dlatego nie traktuję go jako „lepszego stringa”, tylko jako osobny mechanizm z własnymi zasadami.
-
Brak kotwic - wzorzec bez
^i$może dopasować tylko fragment tekstu, którego wcale nie chciałeś zaakceptować. -
Zbyt duże pole dopasowania -
.*wygląda wygodnie, ale często przepuszcza więcej niż powinien. -
Nieucieczone znaki specjalne - kropka, nawiasy i znak zapytania mają znaczenie, więc w tekście dosłownym trzeba je poprzedzić
\. -
Założenie, że
\wrozumie polskie znaki - w praktyce to bywa zdradliwe, zwłaszcza w JavaScripcie; jeśli potrzebujesz pełnego Unicode, lepiej świadomie sięgnąć po odpowiednie klasy lub właściwości znaków. - Testowanie implementacji zamiast zachowania - regex dopasowany do klasy CSS albo technicznego identyfikatora może się sypać po refaktorze, mimo że funkcjonalnie nic się nie zmieniło.
Dobry przykład ryzyka to wzorzec /Zamówienie/i. Taki regex przejdzie zarówno dla komunikatu „Zamówienie złożone”, jak i dla „Anulowano zamówienie”, a więc nie mówi już prawie nic o tym, co naprawdę chcesz sprawdzić. Dużo lepiej wygląda /^Zamówienie #\d+$/i, bo ogranicza dopasowanie do konkretnej struktury i wymusza jasny kontekst.
Jeśli tekst w DOM łamie się na kilka linii albo zawiera dodatkowe spacje, regex też może zacząć zachowywać się inaczej, niż się spodziewasz. W takich sytuacjach patrzę nie tylko na wzorzec, ale też na to, skąd dokładnie pochodzi tekst: z textContent, z renderowanego innerText czy z agregacji kilku węzłów. To drobiazg, ale w testach właśnie takie detale decydują o stabilności.
Po wyłapaniu tych pułapek zostaje najważniejsze pytanie: kiedy regex rzeczywiście jest najlepszym wyborem, a kiedy lepiej go odpuścić.
Kiedy lepiej postawić na prostsze sprawdzenie
Ja używam regexu tylko wtedy, gdy daje on realną przewagę. Jeśli element ma stały tekst, prostsza asercja będzie czytelniejsza i mniej podatna na nieporozumienia. Jeśli można użyć dobrego selektora po roli, etykiecie albo data-testid, to zwykle robię to najpierw, a regex zostawiam na warstwę treści, która faktycznie jest zmienna.
- Jeśli tekst jest stały, wybieram dokładne porównanie zamiast wzorca.
- Jeśli szukam elementu na stronie, najpierw używam roli, etykiety albo test id, a dopiero później tekstu z regexem.
- Jeśli sprawdzam odpowiedź API, często lepiej rozbić JSON na pola niż próbować objąć wszystko jednym wzorcem.
- Jeśli regex zaczyna wymagać długiego komentarza, to zwykle znak, że stał się zbyt złożony jak na test.
W praktyce najlepszy kompromis wygląda tak: selektor znajduje właściwy element, a regex sprawdza tylko tę część treści, która naprawdę bywa zmienna. Dzięki temu test pozostaje czytelny dla zespołu i jednocześnie odporny na drobne różnice w danych.
Właśnie taki dobór daje najlepszy efekt w automatyzacji testów: krótsze, bardziej zrozumiałe sprawdzenia, które bronią się w codziennym utrzymaniu i nie wymagają przepisywania po każdej kosmetycznej zmianie interfejsu.