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:
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ę
toHaveText
moż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 😀