Powrót do: Praktyczne wprowadzenie do testów automatycznych z Playwright
Rozwiązanie: Implementacja AAA
Dodatkowe materiały
Bazujemy na kodzie lekcji L03_trace_viewer
Kod wynikowy tej lekcji znajduje się tu: L05_rozwiazanie_aaa
Pamiętaj, aby po każdej większej modyfikacji uruchamiać testy 😉
Cel zadania
Naszym zadaniem była kontynuacja refaktoryzacji testów w oparciu o wzorzec AAA (Arrange Act Assert)
Refaktoring kodu testów w login.spec.js
W pierwszym kroku w pliku login.spec.ts zmieniliśmy jako pierwszy test z niepoprawną nazwą użytkownika:
test('unsuccessful login with too short username', async ({ page }) => {
await page.goto('https://demo-bank.vercel.app/');
await page.getByTestId('login-input').fill('tester');
await page.getByTestId('password-input').click();
await expect(page.getByTestId('error-login-id')).toHaveText(
'identyfikator ma min. 8 znaków'
);
});
na:
test('unsuccessful login with too short username', async ({ page }) => {
// Arrange
const url = 'https://demo-bank.vercel.app/';
const incorrectUserId = 'tester';
const expectedErrorMessage = 'identyfikator ma min. 8 znaków';
// Act
await page.goto(url);
await page.getByTestId('login-input').fill(incorrectUserId);
await page.getByTestId('password-input').click();
// Assert
await expect(page.getByTestId('error-login-id')).toHaveText(
expectedErrorMessage
);
});
W kolejnym kroku w pliku login.spec.ts zmieniliśmy kolejny test z:
test('unsuccessful login with too short password', async ({ page }) => {
await page.goto('https://demo-bank.vercel.app/');
await page.getByTestId('login-input').fill('testerLO');
await page.getByTestId('password-input').fill('1234');
await page.getByTestId('password-input').blur();
await expect(page.getByTestId('error-login-password')).toHaveText(
'hasło ma min. 8 znaków'
);
});
na:
test('unsuccessful login with too short password', async ({ page }) => {
// Arrange
const url = 'https://demo-bank.vercel.app/';
const userId = 'testerLO';
const incorrectPassword = '1234';
const expectedErrorMessage = 'hasło ma min. 8 znaków';
// Act
await page.goto(url);
await page.getByTestId('login-input').fill(userId);
await page.getByTestId('password-input').fill(incorrectPassword);
await page.getByTestId('password-input').blur();
// Assert
await expect(page.getByTestId('error-login-password')).toHaveText(
expectedErrorMessage
);
});
Refaktoring kodu testów w pulpit.spec.js
W pliku pulpit.spec.ts zmieniliśmy test, który jeszcze nie był zmodyfikowany z:
test('successful mobile top-up', async ({ page }) => {
await page.goto('https://demo-bank.vercel.app/');
await page.getByTestId('login-input').fill('testerLO');
await page.getByTestId('password-input').fill('password');
await page.getByTestId('login-button').click();
await page.locator('#widget_1_topup_receiver').selectOption('500 xxx xxx');
await page.locator('#widget_1_topup_amount').fill('50');
await page.locator('#uniform-widget_1_topup_agreement span').click();
await page.getByRole('button', { name: 'doładuj telefon' }).click();
await page.getByTestId('close-button').click();
await expect(page.locator('#show_messages')).toHaveText(
'Doładowanie wykonane! 50,00PLN na numer 500 xxx xxx'
);
});
na:
test('successful mobile top-up', async ({ page }) => {
// Arrange
const url = 'https://demo-bank.vercel.app/';
const userId = 'testerLO';
const userPassword = '10987654';
const topUpReceiver = '500 xxx xxx';
const topUpAmount = '50';
const expectedMessage = `Doładowanie wykonane! ${topUpAmount},00PLN na numer ${topUpReceiver}`;
// Act
await page.goto(url);
await page.getByTestId('login-input').fill(userId);
await page.getByTestId('password-input').fill(userPassword);
await page.getByTestId('login-button').click();
await page.locator('#widget_1_topup_receiver').selectOption(topUpReceiver);
await page.locator('#widget_1_topup_amount').fill(topUpAmount);
await page.locator('#uniform-widget_1_topup_agreement span').click();
await page.getByRole('button', { name: 'doładuj telefon' }).click();
await page.getByTestId('close-button').click();
// Assert
await expect(page.locator('#show_messages')).toHaveText(expectedMessage);
});
Dodatkowe ustawienia w Prettier
Aby uniknąć problemów z sygnalizowaniem pustych zmian w zakładce Source Control dodaliśmy do pliku .prettierrc.json dodatkową opcję
"endOfLine": "auto"
Cały plik ma teraz postać:
{
"singleQuote": true,
"endOfLine": "auto"
}
Formatowanie końca linii zostawiliśmy domyślnym ustawieniom narzędzia Git dzięki czemu nie będziemy już otrzymywać pustych zmian w plikach.

Hej, mam problem z prettierem, mój prettier.json wygląda tak
{
“singleQuote”: true,
“endOfLine”: “auto”
}
Mam zainstalowaną poprawny plugin z prettierem do VS Code.
Ale kiedy używam alt + shift + f, to prettier łamie mi linie w asercjach, czy jest na to jakiś sposób? Może coś przeoczyłem?
await expect(page.getByTestId(‘message-text’)).toHaveText(
finalConfirmationMsg,
);
});
Hej,
Jaką masz wersję prettiera zainstalowaną w projekcie w package.json i jaką masz wersję wtyczki w VS Code?
Ja właśnie sprawdziłem to ustawienie na wersji paczki 3.2.4 i na wersji wtyczki v10.1.0 i wszystko działa poprawnie (tzn – nie łamie linii w asercjach) 🤔
Hej
package-json:
“prettier”: “3.2.4”
wtyczka:
v10.1.0 (40,387,273 pobrań)
Nie wiem czy to na dłuższą metę jest dobry pomysł, ale znalazłem na stackoverflow taką podpowiedź i teraz działa, dodałem do configu prettiera tę linię zwiekszając maksymalną dopuszczalną szerokość
“printWidth”: 100
z drugiej strony zauważyłem, że jak formatuje dluższą linijkę, gdzie chcę żeby była złamana, prettier łamie ją (i to jest ok) ale dodaje też przecinek na końcu, nie wiem czemu. Po “{transferTitle}`” jest dodany przecinek przez prettiera. Mimo tego przecinka, test spokojnie przechodzi. Dziwne, czy to może tak zostać?
await expect(page.locator(‘#show_messages’)).toHaveText(
`Przelew wykonany! ${expectedTransferReceiver} – ${transferAmount},00PLN – ${transferTitle}`,
);
Rozumiem, że chodzi o łamanie w taki sposób:
await expect(page.getByTestId('message-text')).toHaveText( finalConfirmationMsg, );To domyślne zachowanie Prettier, że przy liniach powyżej 80 znaków szuka możliwości ich złamania i to jest ok. Zwykle nie chcemy mieć długich linii bo są trudne w czytaniu i w projektach raczej trzymamy się tego standardu.
Co do przecinka to najnowsza implementacja Prettier zakłada, że przy obiektach dodaje zawsze kończący przecinek przy parametrach w wielu liniach (nawet przy jednym).
Ostatni przecinek daje nam zabezpieczenie, że możemy dodać kolejny parametr bez pamiętania o umieszczenia przecinka w poprzedniej linii.
Biorąc np. metodę
toHaveTextmożemy do niej dodać oprócz tekstu asercji jeszcze kolejne elementy jak opcje związane z timeout, inner text etchttps://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-text
Super, dzięki za odpowiedź. W takim razie “printWidth”: 100, rozumiem, że to przywróci wartość domyślną 80, a przecinki zostawiam w spokoju 🙂
Miało być “W takim razie usunę “printWidth”: 100, …” 😀
Super – eksperymentuj z najlepszą formą ☺️
Pewnie odpowiedź będzie w kolejnych lekcjach i będzie w stylu że zmienne trzymamy w osobnym pliku ale czy w tej lekcji, jeśli zmienna url jest używana w kilku przypadkach testowych, to czy można ją wyciągnąć przed wszystkie testy i używać powiedzmy jako zmiennej globalnej?
Bardzo dobre pytanie!
Słusznie podejrzewasz – że należałoby ją wydzielić do zmiennej np. jako zmiennej globalnej 🙂 Dzięki temu unikamy powtórzenia danej wartości i stosujemy zasadę DRY, czyli Dont Repeat Yourself. A w rezultacie – łatwiej nam utrzymywać nasze testy 🙂
Taki refactor jest naturalnym procesem, bo:
– czasem na początku chcemy, aby testy szybko przyniosły nam wartość
– z czasem dostrzegamy elementy, które można usprawnić 🙂
PS. Już w kolejnej lekcji zajmujemy się tą zmienną – zaczynając tak jak podejrzewałeś – czyli od zmiennej globalnej 😉
Po chwili od napisania komentarza uruchomiłem kolejną lekcję i wszystko stało się jasne (nawet przez chwilę chciałem usnąć ten komentarz 😀 ale nie ma takiej opcji więc niech zostanie dla potomnych) ale dzięki za konkretną odpowiedź 🙂
Doskonale 😀