Rozwiązanie: Implementacja AAA

TIP: Ta lekcja jest częścią rozwijanego Programu Testy Automatyczne z Playwright 🎭

Dodatkowe materiały

TIP: Na koniec tej lekcji usprawniamy konfigurację Prettier! Warto dodać to do twojego projektu.

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

TIP: Do zmiany nazwy zmiennej zalecamy użyć opcji (prawy klawisz myszki na nazwie zmiennej) Rename symbol lub skrót F2.

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.

12 komentarzy

  1. 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,
    );
    });

    Avatar Franciszek Klocek
    1. 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) 🤔

      Krzysiek Kijas Krzysiek Kijas
      1. 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}`,
        );

        Avatar Franciszek Klocek
        1. 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ę toHaveText możemy do niej dodać oprócz tekstu asercji jeszcze kolejne elementy jak opcje związane z timeout, inner text etc
          https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-have-text

          Przemek Przemek
  2. 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?

    Avatar Michał Żak
    1. 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 😉

      Krzysiek Kijas Krzysiek Kijas
      1. 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ź 🙂

        Avatar Michał Żak

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *