W badaniu Sii Testing Lab opisaliśmy siedem kluczowych wniosków z naszego Hackathonu poświęconego roli AI w automatyzacji testów. Publikacja spotkała się z ogromnym zainteresowaniem, a liczba pytań o techniczne szczegóły, które od Was otrzymaliśmy, przerosła nasze oczekiwania. Właśnie dlatego zdecydowaliśmy się przygotować ten komentarz.
Choć w raporcie potwierdziliśmy, że AI podnosi efektywność, doświadczenie testera ma istotne znaczenie, a modele mają swoje ograniczenia, to tamte analizy (z natury rzeczy) pozostały na poziomie uogólnionych wniosków.
Ten artykuł to krok dalej. Schodzimy poziom niżej: do kodu, konkretnych decyzji implementacyjnych i momentów, w których wyraźnie widać granicę między możliwościami narzędzia a krytycznym myśleniem inżyniera.
Aby skupić się na tym, co ważne – na wzorcach i jakości kodu, a nie na rankingach – omawiam repozytoria anonimowo. Zależy mi na wyciągnięciu uniwersalnych lekcji, a nie na ocenie pracy poszczególnych zespołów.
Najpierw przypomnijmy, co ocenialiśmy
Każde repozytorium było oceniane według ośmiu kryteriów jakości:
- K1 – Zgodność testu z celem biznesowym i pokrycie wymagań.
- K2 – Dane testowe i przygotowanie stanu.
- K3 – Stabilność rozwiązania.
- K4 – Jakość selektorów i lokalizatorów.
- K5 – Architektura testu i wzorce.
- K6 – Jakość asercji.
- K7 – Diagnostyka i obsługa błędów.
- K8 – Engineering.
Każde repozytorium zostało oceniane w skali pięciostopniowej, jednak oceny te należy interpretować przez pryzmat specyfiki badania.
Porównywaliśmy dwa podejścia:
- zespoły automatyzujące bez użycia AI (Oldschool),
- zespoły automatyzujące z użyciem AI (AI).
Obie grupy miały na pracę zaledwie 6 godzin, co determinowało sposób punktowania. Jury celowo przymykało oko na nieukończone moduły, priorytetyzując dojrzałość inżynierską oraz jakość tego, co zostało zrealizowane. Warto jednak zaznaczyć, że dla grupy AI, która dzięki technologii narzuciła znacznie szybsze tempo, poprzeczka była postawiona odpowiednio wyżej.
Grupa Oldschool: Dobra robota w trudnych warunkach
Zanim wejdziemy w szczegóły kodu, ważna uwaga kontekstowa: sześć godzin to naprawdę krótki czas na zbudowanie czegokolwiek od zera. Decyzje, które wyglądają jak uproszczenia, często były świadomymi kompromisami wynikającymi z presji czasowej, a nie z braku wiedzy czy kompetencji.
Mając to w pamięci, przyjrzyjmy się temu, co zespoły Oldschool faktycznie dostarczyły.
Testy przechodzą, ale co weryfikują?
W repozytoriach Oldschool dominuje jeden powtarzający się wzorzec: sekwencja kroków UI zakończona asercją widoczności elementu. Testy działają, obejmują główne ścieżki użytkownika: logowanie, koszyk, checkout. Na pierwszy rzut oka wszystko wygląda poprawnie.
Problem ujawnia się, gdy zadamy pytanie: Co tak naprawdę jest tutaj weryfikowane?
// Typowy test z grupy Oldschool*
public void OrderPlacement() {
UserIsLoggedIn();
UserHasProductsAddedToCart();
_productPage.OpenBasket();
_productPage.ProceedWithOrder();
_addressFormPage.ClickContinue();
// ...
Assert.That(_orderConfirmationPage.ConfirmationMessage);
}
* Przykład 1:1
Test potwierdza, że proces się zakończył, ale nie potwierdza, czy zakończył się poprawnie. Brakuje weryfikacji ceny, sprawdzenia zastosowanych rabatów, poprawnego naliczenia podatków. Testy sprawdzają przepływ, nie logikę biznesową.
Jak mógłby wyglądać ten sam test napisany z myślą o wartości biznesowej?
// Test weryfikujący logikę biznesową*
[Test]
public void Should_Add_One_Product_To_Basket() {
CreatedProduct product = PrestashopTestDataService.CreateProductWithQuantity();
Driver.GoToUrl(Urls.Product(product.Url));
At<ProductDetailsPage>(x => x.AddToCart());
At<CartPopupPage>(x => {
x.ProductName.Should().Be(product.Name);
x.ProductQuantity.Should().Be("1");
});
}
* .Be(„1”) zastosowane jako uproszczenie do zaprezentowania koncepcji
Różnica jest zasadnicza: zamiast sprawdzać, czy pojawił się komunikat o sukcesie, weryfikujemy konkretne wartości domenowe – nazwę produktu i ilość.
Warto odnotować pozytywny wyjątek w drugą stronę: jeden z zespołów Oldschool napisał testy parametryzowane z negatywnymi przypadkami, czego nie zrobił żaden zespół AI. Przykład pokazuje świadomość, że warto testować nie tylko happy path:
// Testy negatywne z parametryzacją (Oldschool)*
@ParameterizedTest
@MethodSource("provideIncorrectUsers")
public void verifyAccountRegistrationWithoutMandatoryFields(
User user, String description) {
registrationLoginSteps.verifyUserIsNotLoggedIn();
}
private static Stream<Arguments> provideIncorrectUsers() {
return Stream.of(
Arguments.of(DataProvider.getUserWithoutMandatoryField(), "..."),
Arguments.of(DataProvider.getUserWithIncorrectEmail(), "..."),
Arguments.of(DataProvider.getUserWithTooShortPassword(), "..."),
Arguments.of(DataProvider.getUserWithAlreadyRegisteredEmail(), "...")
);
}
* Przykład 1:1
To ciekawy przykład myślenia o tym, co może pójść nie tak. AI skupiało się głównie na happy path. Stary odruch testerski, którego modele językowe nie nabyły.
Interfejs zamiast API: świadomy kompromis, ale z konsekwencjami
W większości repozytoriów Oldschool setup danych opiera się na UI. To zrozumiałe przy ograniczonym czasie integracja z API wymaga dodatkowej infrastruktury. Jednak koszt tego wyboru jest widoczny w kodzie:
// Setup przez UI (szybki do napisania, kruchy w utrzymaniu)*
[SetUp]
public void Setup() {
UserIsLoggedIn();
_loggedUser.IsUserLoggedIn();
UserHasProductsAddedToCart(); // nawiguje przez UI do produktu
}
* Przykład 1:1
Każda zmiana w interfejsie administracyjnym może wywołać efekt domina, kładąc nie pojedynczy test, ale cały setup. Testy są ze sobą powiązane i trudne do izolacji. Choć pod presją czasu taki kompromis jest wybaczalny, w projekcie produkcyjnym trzeba by go prędzej czy później adresować.
Decyzję pokrycia tej warstwy podjęły dwa z pięciu zespołów Oldschool. Zespoły te, mimo ograniczonego czasu, zdecydowały się zainwestować w setup danych oparty na API, równolegle do pracy nad warstwą UI.
W praktyce oznaczało to podział na specjalizacje, gdzie jedna osoba rozwijała warstwę UI, podczas gdy druga budowała setup oparty na API. Efektem była wyraźnie wyższa jakość i stabilność danych testowych. Co istotne, taka decyzja nie była najłatwiejszą drogą – wymagała większej koordynacji, szczególnie w obszarze integracji zmian i spójności implementacji. Postawienie na stabilność kosztem szybkości implementacji to świadoma decyzja, która najlepiej świadczy o warsztacie zespołów.
To dowód na to, że inżynierska odpowiedzialność i rozumienie ryzyk wzięły górę nad presją czasu.
Stabilność i selektory: Działają lokalnie, a w CI?
W obszarach K3 i K4 pojawia się ukryte ryzyko. Część zespołów używa hardcodowanych waitów zamiast inteligentnych waitów:
// Hardcode wait (typowe źródło flakiness)*
Thread.Sleep(1000);
var continueButton = DriverProvider.Driver.FindElement(
By.Name("confirmDeliveryOption"));
continueButton.Click();
// Propozycja: Lepsze podejście (czekanie na konkretny stan)
await page.click('#checkout');
await expect(page.locator('#payment')).toBeVisible();
* Przykład 1:1
Podobnie z selektorami. Zamiast stabilnych atrybutów testowych pojawiają się krótkie, strukturalnie zależne selectory:
// Kruchy selector, zależność od treści przycisku*
private IWebElement cartButton => DriverProvider.Driver
.FindElement(By.XPath("//a[@href='//145.239.29.97/cart?action=show']"));
private IWebElement continueShopping => DriverProvider.Driver
.FindElement(By.XPath("//button[@data-dismiss='modal' and contains(.,'Continue shopping')]"));
* Przykład 1:1
Testy działają lokalnie. Pytanie, jak zachowują się przy drobnych zmianach UI lub w wolniejszym środowisku CI, pozostaje otwarte. W tym przypadku selektor powinien być przywiązany do intencji elementu (co robi), nie do jego reprezentacji (jak wygląda lub co mówi). Zmiana języka interfejsu, środowiska czy layoutu nie powinna łamać testu.
Page Objecty mówią językiem kliknięć, nie biznesu
Architektura w grupie Oldschool jest poprawna: pojawiają się Page Objecty, kod jest czytelny, struktura ma sens. Jednak Page Objecty pełnią głównie rolę technicznych wrapperów, nie warstwy domenowej:
// Wrapper bez logiki domenowej*
@Step("Click 'Continue' button in Address section")
public void clickContinueButtonInAddressSection() {
addressContinueButton.click();
}
@Step("Click 'Place order' button")
public void clickPlaceOrderButton() {
placeOrderButton.click();
}
// Propozycja: metoda domenowa łącząca kroki i weryfikująca wynik
public Order placeOrder(Product product) {
clickContinueButtonInAddressSection();
clickContinueButtonInShippingMethodSection();
selectCashPaymentRadioButton();
acceptTermsAndConditions();
clickPlaceOrderButton();
return orderConfirmationPage.getOrderDetails(); // zwraca obiekt domenowy
}
* Przykład 1:1
W pierwszym przypadku test opisuje, jak kliknąć. W drugim sprawdza, jak działa system, co wpływa fundamentalnie na wartość testu. Zmiana UI (np. nowy krok w checkout) wymaga zmiany tylko w jednym miejscu, a nie we wszystkich testach.
Asercje: fałszywe poczucie bezpieczeństwa
Najbardziej powtarzalnym problemem w repozytoriach Oldschool jest jakość asercji. Dominują konstrukcje sprawdzające widoczność lub truthiness zamiast konkretnych wartości biznesowych:
// Asercja sprawdzająca tylko widoczność*
Assert.That(_orderConfirmationPage.ConfirmationMessage);
// Propozycja: Asercja sprawdzająca wartość domenową
updated.StockAvailable.Quantity.Should().Be(updatedQuantity);
updated.StockAvailable.ProductId.Should().Be(productId);
* Przykład 1:1
Efekt jest paradoksalny: testy przechodzą, wyglądają poprawnie, ale nie chronią systemu przed regresją, dając fałszywe poczucie bezpieczeństwa wbudowane w pipeline.
Żeby pokazać pełne spektrum grupy Oldschool: na drugim końcu skali znajduje się repozytorium, które zawierało jeden plik, niezmodyfikowany szablon wygenerowany przez Playwright CLI, testujący stronę playwright.dev, nie PrestaShop:
// Jedyny test w repozytorium, domyślny szablon z playwright.dev*
public async Task HomepageHasPlaywrightInTitleAndGetStartedLink()
{
await Page.GotoAsync("https://playwright.dev"); // <-- nie PrestaShop
await Expect(Page).ToHaveTitleAsync(new Regex("Playwright"));
var getStarted = Page.Locator("text=Get Started");
await getStarted.ClickAsync();
await Expect(Page).ToHaveURLAsync(new Regex(".*intro"));
}
Przykład 1:1 – skrócona nazwa metody bez wpływu na wynik
Może się wydawać, że to złośliwa obserwacja, jednak jest to jeden z przykładów, o którym mówi nasz raport: bez AI część zespołów spala dzień na pojedynczym problemie technicznym. Ktoś utknął na setupie środowiska i sześć godzin zniknęło. Dwa zespoły w tej samej grupie, z wynikami 4,3 i 2,9 – to jest prawdziwy pokaz rozpiętości wyników grupy Oldschool.
Wyjątek, który rządził całą grupą
Przy wszystkich tych obserwacjach warto zaznaczyć, że w grupie Oldschool pojawiła się różnica, która nie jest minimalna. Jedno repozytorium (z wynikiem 4,3/5) było nie tylko najlepszym w grupie Oldschool, ale najlepszym wynikiem w całym badaniu, powyżej wszystkich zespołów AI.
To repozytorium wyróżniało się na każdym poziomie:
- architektura pięciu osobnych projektów (Core, Api, Ui, TestSupport, Tests),
- dedykowany serwis danych,
- pełny cleanup po każdym teście,
- zero sekretów w repozytorium.
Wrócimy do niego szczegółowo przy analizie wzorców.
Czy obecność architekta zrobiła różnicę?
W obu grupach rozkład sił był jednakowy: cztery zespoły Regular+Regular i jeden z osobą o znacznie wyższym doświadczeniu.
W grupie Oldschool różnica była zauważalna. Repozytorium z architektem wyróżniało się staranniejszą strukturą, przemyślanymi abstrakcjami i dyscypliną inżynierską widoczną w każdym pliku. Było czuć, że ktoś w zespole wie, jak to powinno wyglądać.
Ale i tu pojawia się kluczowa obserwacja: samo doświadczenie, bez wsparcia AI, nie wystarczyło do zmiany fundamentalnego podejścia. Presja czasowa sześciogodzinnego Hackathonu spychała wszystkich w kierunku podobnych kompromisów.
Doświadczenie poprawia jakość implementacji. Dopiero AI sprawia, że doświadczenie zaczyna definiować cały wynik.
Grupa AI: Inne podejście, te same pułapki (i jeden paradoks)
Struktura od pierwszego commita
Kiedy otwieramy repozytoria zespołów AI, uderza jedno: wysoki poziom struktury od samego początku. Testy są lepiej nazwane, logicznie pogrupowane i od razu osadzone w architekturze. Nie ma etapu „zacznijmy cokolwiek, potem posprzątamy”.
Widać to w tym, jak wyglądają klasy bazowe, jak zorganizowane są fixtures, jak konsekwentnie stosowane są wzorce.
Porównanie jest uderzające:
// Oldschool (test zaczyna się od szczegółów UI)*
public void OrderPlacement() {
var url = Config["BaseUrl"] + "home-accessories/7-mug.html";
DriverProvider.Driver.Navigate().GoToUrl(url);
_productPage.AddProductToCart();
Thread.Sleep(1000);
var continueButton = DriverProvider.Driver.FindElement(
By.Name("confirmDeliveryOption"));
// ...
Przykład – kompozycja z dwóch metod dla zaprezentowania koncepcji
// AI (test zaczyna się od celu)*
@Test
@Description("End-to-end order: add product, fill checkout, pay, verify")
void shouldPlaceOrder_whenPayingByBankWire() {
open(UIRoutes.HOME);
homePage.clickProductByName(EXISTING_PRODUCT);
productPage.clickAddToCart();
// ...
assertThat(orderConfirmationPage.getOrderReference())
.as("Order reference should be present")
.isNotBlank();
}
Przykład 1:1 (skrócony)
AI jako akcelerator architektury
Największy skok jakościowy w grupie AI widać w architekturze (K5). Pojawiają się warstwy serwisowe, świadome fixtures, separacja odpowiedzialności, czyli rzeczy, które w Oldschool były wyjątkiem, tu stają się standardem:
// Fixture z pełnym lifecycle'em typowe dla grupy AI*
public class HybridFixture : TracingFixture {
protected UserFactory UserFactory;
protected AuthService AuthService;
[SetUp]
public async Task SetUpHybridContext() {
ApiFactory = new PrestaShopApiFactory(AdminApiContext);
UserFactory = new UserFactory(ApiFactory);
AuthService = new AuthService(Playwright);
}
[TearDown]
public async Task TearDownHybridContext() {
await ApiFactory.CleanupAsync();
await AdminApiContext.DisposeAsync();
}
}
Przykład 1:1 (skrócony)
AI pozwala zespołom bardzo szybko wejść na poziom, który normalnie wymaga czasu i wielu iteracji projektowych. Skraca drogę od „u mnie działa” do „jest zdefiniowane”.
Ale tu pojawia się też pierwszy problem. Część tej architektury jest poprawna formalnie, lecz płytka funkcjonalnie, generując struktury, które wyglądają profesjonalnie, ale bez świadomego human-in-the-loop nie służą niczemu konkretnemu.
Asercje: Ta sama pięta achillesowa co w Oldschool
Mimo lepszej struktury jeden problem pozostaje niemal niezmieniony: głębokość walidacji biznesowej. Nawet w najlepszych repozytoriach AI asercje są często zbyt ogólne:
// Asercja, która potwierdza, że coś istnieje*
assertNotNull(actual.getId(), "Customer ID should not be null");
assertNotNull(actual.getId(), "New customer ID should be returned");
// Propozycja: Asercja, która potwierdza, że działa poprawnie
assertThat(orderConfirmationPage.getOrderReference())
.isNotBlank();
assertThat(displayedPrice).isEqualByComparingTo(expectedGrossPrice);
Przykład 1:1
Jeden z zespołów AI poszedł dalej i zbudował dedykowaną klasę PriceUtils do weryfikacji wartości brutto po zastosowaniu VAT. To rzadki przykład asercji na poziomie logiki biznesowej w tej grupie.
AI poprawia sposób pisania testów, ale nie wymusza tego, co testy powinny sprawdzać.
Dane testowe i niespodzianka z sekretami
W obszarze danych testowych (K2) zespoły AI robią krok naprzód: pojawia się API setup, myślenie o izolacji, unikalne dane generowane przez factory. Ale konsekwencja bywa zaburzona – w jednym pliku widzimy wzorcowe podejście, natomiast kilka plików dalej wyłania się setup przez UI.
Jest też obszar, gdzie grupa AI wypadła gorzej niż Oldschool: bezpieczeństwo sekretów. Dwa repozytoria AI miały API key bezpośrednio w plikach konfiguracyjnych zacommitowanych do repo. Jedno z nich zawierało przy tym komentarz:
// Obrazowy przykład sekretów*
# Ideally, this should be stored in secrets
# but let's keep it here for simplification
api.key= tutaj należy wpisać API
admin.password=to miejsce na wpisanie hasła
* Przykład 1:1 (z usuniętymi danymi wrażliwymi)
Zespoły Oldschool radziły sobie z tym lepiej, brak sekretów w repo był tam normą (lata doświadczenia?). To pokazuje, że AI przyspiesza implementację, ale nie zastępuje świadomości inżynierskiej w każdym obszarze.
Cztery wzorce w grupie AI
Analizując pięć repozytoriów AI, widać wyraźnie, że nie tworzą jednorodnej grupy. Wyłaniają się cztery różne wzorce, które nazwałbym:
1. „Fabryka testów” – imponująca liczba testów (blisko 200), testy hybrydowe API+UI, fixtures z cleanup. Największy wolumen w całym badaniu. Słabość: sekrety w repo i miejscami płytkie asercje (ToContainTextAsync zamiast weryfikacji wartości) oraz testy API ograniczone do Statuscode 200.
2. „Solidny rzemieślnik” – 37 testów, Allure, dedykowana klasa PriceUtils do weryfikacji VAT, asercje na konkretne wartości. Najlepsza średnia jakościowa w grupie AI. Brak API do setupu danych, czyli rejestracja odbywa się przez UI, co jest świadomym uproszczeniem.
Ciekawostka: Zespół ten borykał się z obsłużeniem wspomnianych w oryginalnym raporcie selektorów dla dynamicznych przycisków. To przepaliło część zadanego czasu, przez co wymagało pewnych poświęceń w pokryciu. Nie był w tym jednak odosobniony – wzorzec ten często wracał w zespołach AI.
3. „Szeroki zasięg” – 60 testów API pokrywających kupony, waluty, promocje, katalog. Szeroki zakres, ale asercje miejscami operują na surowym JSON zamiast na deserializowanych obiektach.
Przykład: zamiast deserializować odpowiedź i sprawdzać właściwość obiektu, test porównuje fragment surowego stringa:
// Asercja na surowym JSON (krucha i nieczytelna)*
Assert.That(body, Does.Contain("\"gift_product\":\"11\"").Or.Contain("\"gift_product\":11"),
Assert.That(body, Does.Contain("\"minimum_amount\":\"80").Or.Contain("\"minimum_amount\":80"),
Assert.That(body, Does.Contain("\"active\":\"1\"").Or.Contain("\"active\":1"),
// Propozycja: deserializacja i asercja na właściwości
var coupon = JsonSerializer.Deserialize<CartRule>(body);
Assert.That(coupon.Active, Is.True);
Assert.That(coupon.ReductionAmount, Is.EqualTo(5.0m));
* Przykład 1:1
Taka asercja przejdzie, nawet jeśli format JSON się zmieni, bo szuka patternu w stringu, nie weryfikuje wartości domenowej. Co ciekawe, przykład pokazuje, że zespół był na tyle świadomy problemu, że w wielu miejscach dodaje .Or.Contain(…) z alternatywną wersją bez cudzysłowów. To nie rozwiązuje problemu, ale pokazuje, że ktoś zdawał sobie sprawę z kruchości.
4. „Dokumentacja zamiast kodu” – imponujące artefakty: plany testów w .claude/, self-evaluation po każdym kryterium K1-K8, szczegółowe scenariusze TC-001 do TC-030. Ale 5 testów w repozytorium. AI świetnie poradziło sobie z planowaniem – niestety, czas skończył się zanim przyszło do implementacji.
Uwaga: to nie jest wzorzec nieudany, ale ostrzeżenie dla całej branży. AI było tak dobre w planowaniu, że zespół spędził większość czasu na budowaniu mapy zamiast iść w teren. To ciekawe zjawisko, gdzie AI generuje tak przekonujące artefakty planistyczne, że implementacja schodzi na drugi plan.
Paradoks tego badania: najlepsze repozytorium pochodzi z grupy Oldschool
Tu dochodzimy do najważniejszego odkrycia całej analizy i do miejsca, w którym proste narracje o AI muszą ustąpić faktom.
Najlepsze repozytorium w całym badaniu (z wynikiem 4,3/5) pochodzi z grupy Oldschool. Pobiło wszystkie pięć repozytoriów AI. I jest dokładnie tym, co artykuł o grupie AI opisywał jako niedościgły wzorzec: API jako podstawa danych, testy hybrydowe, pełny cleanup, walidacja domenowa.
// Wzorcowy setup danych przez API z grupy Oldschool*
public CreatedProduct CreateProductWithQuantity(
int quantity = TestDataConstants.Product.DefaultAvailableQuantity) {
CreateProductRequest request = CreateProductRequestFactory.CreateValid(settings);
ProductEnvelope response = productsApi.CreateProduct(request);
var productId = response.Product.Id
?? throw new InvalidOperationException("Product id not returned");
cleanupTracker.Track(productId, productsApi); // automatyczny cleanup
stockAvailablesApi.UpdateQuantity(stockAvailableId, quantity);
return new CreatedProduct { Request = request, Response = response };
}
* Przykład – kompozycja metod dla zaprezentowania koncepcji
// Asercje na wartości domenowe z grupy Oldschool*
At<CartPopupPage>(x => {
x.ProductName.Should().Be(product.Name);
x.ProductQuantity.Should().Be("1");
});
updated.StockAvailable.Quantity.Should().Be(updatedQuantity);
updated.StockAvailable.ProductId.Should().Be(productId);
* Przykład – kompozycja metod dla zaprezentowania koncepcji
// ResourceCleanupTracker - automatyczne sprzątanie po testach*
public class ResourceCleanupTracker {
public void Cleanup() {
foreach (var item in _resources.DistinctBy(x => (x.Id, x.Api)).ToList())
TryDelete(() => item.Api.DeleteById(item.Id));
}
}
* Przykład 1:1 z uproszczeniem w postaci var item – różnica jedynie stylistyczna
Pięć osobnych projektów (Core, Api, Ui, TestSupport, Tests), dedykowany generator danych z GUID-em w każdym e-mailu, konfiguracja przez User Secrets bez ani jednego sekretu w repo, automatyczne screenshoty przy failach.
To opis najlepszego repozytorium w całym badaniu (napisanego bez AI), przez zespół z architektem, w sześć godzin. Można więc wysnuć następujący wniosek:
AI podnosi poziom wejścia dla wszystkich, ale szczyt nadal należy do doświadczenia.
Warto zatrzymać się przy zestawieniu, które wynika z danych: 196 testów, 3,6/5 vs 8 testów, 4,3/5. Artykuł to opisuje, ale warto powiedzieć wprost: 8 testów, które weryfikują konkretne wartości domenowe, sprzątają po sobie i izolują dane, jest prawdopodobnie bardziej wartościowe produkcyjnie niż 196 testów z płytkimi asercjami. Duża liczba testów bez jakości daje fałszywsze poczucie bezpieczeństwa niż mała liczba dobrych testów. To wniosek, który menedżerowie i tech leaderzy powinni zabrać z tego badania: coverage to nie licznik, a kryterium jakościowe.
Gdzie doświadczenie naprawdę ma znaczenie
Tu pojawia się główny wniosek dotyczący roli doświadczenia. Należy jednocześnie zaznaczyć, że jest bardziej subtelny niż słyszane w mediach „AI zastępuje doświadczenie” czy „doświadczenie zawsze wygrywa”.
W grupie Oldschool różnica między zespołami Regular+Regular a Architect+Junior była widoczna, ale presja czasowa spłaszczała wyniki. Wszyscy zmierzali w podobnym kierunku, a kompromisy czasowe były zbliżone.
W grupie AI ta różnica stała się decydująca. AI usunęło bariery techniczne: składnię, setup frameworka, podstawowe wzorce. Okazało się, że kiedy te bariery znikają, to co pozostaje, to czyste decyzje architektoniczne (proporcjonalne do doświadczenia).
Trzy obszary, gdzie doświadczenie (za)decyduje
1. Co testujemy, a nie jak testujemy – większość zespołów Oldschool i AI zatrzymała się na weryfikacji widoczności komunikatów. Najlepsze repozytoria z obu grup poszły dalej: weryfikowały konkretne wartości domenowe, poprawność obliczeń VAT, izolację koszyków między użytkownikami.
Pokazuje to nie różnicę w umiejętności pisania kodu, a w świadomym rozplanowaniu, czemu te testy w ogóle służą.
2. Strategia danych testowych – każdy zespół, który podjął świadomą decyzję o API jako warstwie setupu, natychmiast skoczył o poziom jakości. Nie chodzi tylko o szybkość, ale o izolację testów, deterministyczne dane, możliwość równoległego uruchamiania. Niezależnie od składu, jest to efekt doświadczenia zdobytego twardą praktyką (a więc nie wynika stricte z promptowania AI, ale z rozumienia, dlaczego izolacja danych w testach ma znaczenie).
3. Myślenie systemowe, a zbiór testów – większość repozytoriów to zbiór testów. Najlepsze to systemy testowe. Różnica leży w tym, czy architektura pozwala na skalowanie, czy cleanup jest automatyczny, czy warstwy odpowiedzialności są rozdzielone. AI może pomóc szybko zbudować poszczególne elementy tego systemu, ale decyzja o tym, że system ma w ogóle istnieć to ludzka decyzja architektoniczna.
Co AI robi naprawdę dobrze (i gdzie nie dowozi)
Żeby nie było wątpliwości: AI wnosi ogromną, mierzalną wartość. W naszym badaniu różnica w liczbie testów była uderzająca – grupy AI dostarczyły od 5 do niemal 200 testów, Oldschool od 5 do 8. Skrócenie czasu wejścia na dobry poziom jest realne.
AI naprawdę pomaga w:
- Szybkim wejściu na poziom, który Oldschool osiąga po wielu iteracjach.
- Redukcji błędów implementacyjnych i antypatternów.
- Przyspieszeniu wychodzenia z blokad technicznych.
- Budowaniu struktury projektu od pierwszego commita.
- Generowaniu dużej liczby scenariuszy testowych w krótkim czasie.
AI konsekwentnie nie dowozi w:
- Głębokości walidacji biznesowej – nie zapyta, czy sprawdziłeś VAT.
- Strategii danych – nie zadecyduje, że setup powinien być przez API.
- Bezpieczeństwie – nie przeszkodzi zacommitować sekretów do repo.
- Myśleniu systemowym – generuje testy, nie systemy testowe.
- Pragmatyzmie – często generuje „ładny” kod bez realnej wartości.
- Testach negatywnych – skupia się na happy path, pomijając przypadki brzegowe i błędne dane.
Najważniejszy wniosek
To badanie miało jeden wyraźny paradoks: repozytorium, które wszyscy traktowali jako wzorzec dla grupy AI: API setup, hybryda, cleanup, walidacja domenowa – należało do grupy Oldschool.
Ten paradoks mówi nam coś istotnego. AI dramatycznie obniża próg wejścia. Sprawia, że każdy może szybko pisać ustrukturyzowany, poprawny kod. To prawdziwa zmiana.
Ale jednocześnie gdy bariery techniczne znikają, uwypukla się to, co zawsze decydowało o jakości: rozumienie domeny, myślenie architektoniczne, wiedza o tym, co warto testować i dlaczego.
AI znacząco poprawia sposób, w jaki piszemy testy, ale nie poprawia tego, co testujemy.
Dla zespołów oznacza to konkretne wnioski:
- Jeśli nie używasz AI – tracisz na produktywności. Tempo pracy będzie wolniejsze, a liczba dostarczanych scenariuszy niższa.
- Jeśli używasz AI bez zmiany podejścia – będziesz mieć ładniejszy kod, ale testy nadal będą sprawdzać przepływ, nie logikę biznesową.
- Jeśli łączysz AI z doświadczeniem architektonicznym – możesz szybciej zbudować system testowy, który faktycznie chroni produkt.
Największym ryzykiem po tym Hackathonie jest proste założenie: „Skoro mamy AI, jakość sama się poprawi”. Nie poprawi. AI nie zna Twojej domeny, nie rozumie Twojego systemu, nie podejmie za Ciebie decyzji o tym, co warto weryfikować.
Może Ci pomóc szybciej dojść do rozwiązania. Ale to Ty musisz wiedzieć, dokąd idziesz.
AI obniża poziom wejścia, ale jednocześnie podnosi znaczenie doświadczenia na poziomie decyzji.
To badanie pokazuje nam teraźniejszość, do której część zespołów jeszcze nie dotarła.
Automatyzacja testów bez użycia AI stała się podejściem wolniejszym i coraz trudniejszym do uzasadnienia. Podobnie jak testy manualne nie zniknęły po pojawieniu się automatyzacji (ale zmieniły swoją rolę i status), tak klasyczna automatyzacja staje się nowym podejściem manualnym. Wciąż potrzebna, wciąż wartościowa w odpowiednich kontekstach, ale przestaje być domyślnym wyborem dla kogoś, kto chce pracować efektywniej.
Pytanie więc nie brzmi „Czy warto używać AI w automatyzacji”, a „Jak szybko Twój zespół jest w stanie zmienić sposób myślenia o tej pracy”, bo narzędzia już są. Czekają na doświadczonych ekspertów, którzy będą wiedzieć, co z nimi zbudować.
Zostaw komentarz