Kategória: Minőségbiztosítás

Minden, ami tesztelés.

  • Névnapok plugin Gemini-vel generálva

    Gondoltam egy merészet, és megkértem a Gemini-t, hogy készítsen nekem egy WordPress Plugint, ami random Mészáros Lőrinc idézeteket jelenít meg. Megcsinálta.

    “Ha az ember dolgozik, annak megvan az eredménye.”— Mészáros Lőrinc

    Nem álltam itt meg, megkértem, hogy csináljon egy névnap megjelenítőt, az aktuálisra, és a soron következőre.

    Ma: Nárcisz
    Holnap: Alfonz

    Íme:

    Itt egy komplett, egyfájlos WordPress bővítmény, amelyet közvetlenül beépíthetsz a weboldaladba. A bővítmény a látogató böngészőjének aktuális dátumát veszi alapul, kezeli a szökőéveket (a februári névnapeltolódást is), és shortcode segítségével bárhova könnyen elhelyezhető.

    Bővítmény kóda

    Hozz létre egy magyar-nevnapok.php fájlt a /wp-content/plugins/magyar-nevnapok/ mappában, és másold bele az alábbi kódot:

    PHP

    <?php
    /**
     * Plugin Name: Magyar Névnapok (Mai és Holnapi)
     * Description: Megjeleníti a mai és a holnapi névnapot shortcode segítségével.
     * Version: 1.0
     * Author: AI Assistant
     */
    
    if (!defined('ABSPATH')) {
        exit; // Közvetlen hozzáférés tiltása
    }
    
    class Magyar_Nevnapok_Plugin {
    
        // Névnap adatbázis (Hónap -> Nap -> Névnapok)
        private static $nevnapok = [
            1 => [
                1 => 'Fruzsina', 2 => 'Ábel', 3 => 'Genovéva, Benjámin', 4 => 'Titusz, Leona', 5 => 'Simon',
                6 => 'Boldizsár', 7 => 'Attila, Ramóna', 8 => 'Gyöngyvér', 9 => 'Marcell', 10 => 'Melánia',
                11 => 'Ágota', 12 => 'Ernő', 13 => 'Veronika', 14 => 'Bódog', 15 => 'Lóránt, Lóránd',
                16 => 'Gusztáv', 17 => 'Antal, Antónia', 18 => 'Piroska', 19 => 'Sára, Márió', 20 => 'Fábián, Sebestyén',
                21 => 'Ágnes', 22 => 'Vince, Artúr', 23 => 'Zelma, Rajmund', 24 => 'Timót', 25 => 'Pál',
                26 => 'Vanda, Paula', 27 => 'Angéla, Anzelm', 28 => 'Károly, Karola', 29 => 'Adél', 30 => 'Martina, Gerda',
                31 => 'Marcella'
            ],
            2 => [
                1 => 'Ignác', 2 => 'Karolina, Aida', 3 => 'Balázs', 4 => 'Ráhel, Csenge', 5 => 'Ágota, Ingrid',
                6 => 'Dorottya, Dóra', 7 => 'Tódor, Rómeó', 8 => 'Aranka', 9 => 'Abigél, Alex', 10 => 'Elvira',
                11 => 'Bertold, Marietta', 12 => 'Lívia, Lídia', 13 => 'Ella, Linda', 14 => 'Bálint, Valentin', 15 => 'Kolos, Georgina',
                16 => 'Julianna, Lilla', 17 => 'Donát', 18 => 'Bernadett', 19 => 'Zsuzsanna', 20 => 'Aladár, Álmos',
                21 => 'Eleonóra', 22 => 'Gerzson', 23 => 'Alfréd', 24 => 'Mátyás', 25 => 'Géza',
                26 => 'Edina', 27 => 'Ákos, Bátor', 28 => 'Elemér', 29 => 'Névnap szökőévben'
            ],
            3 => [
                1 => 'Albin', 2 => 'Lujza', 3 => 'Kornélia', 4 => 'Kázmér', 5 => 'Adorján, Adrián',
                6 => 'Leonóra, Inez', 7 => 'Tamás', 8 => 'Zoltán', 9 => 'Franciska, Fanni', 10 => 'Ildikó',
                11 => 'Szilárd', 12 => 'Gergely', 13 => 'Krisztián, Ajtony', 14 => 'Matild', 15 => 'Kristóf',
                16 => 'Henrietta', 17 => 'Patrick, Gertrúd', 18 => 'Ede, Sándor', 19 => 'József, Bánk', 20 => 'Klaudia',
                21 => 'Benedek', 22 => 'Beáta, Izolda', 23 => 'Emőke', 24 => 'Gábor, Karina', 25 => 'Irén, Írisz',
                26 => 'Emánuel', 27 => 'Hajnalka', 28 => 'Gedeon, Johanna', 29 => 'Auguszta', 30 => 'Zalán',
                31 => 'Árpád'
            ],
            4 => [
                1 => 'Hugó', 2 => 'Áron', 3 => 'Buda, Richárd', 4 => 'Izidor', 5 => 'Vince',
                6 => 'Vilmos, Bíbor', 7 => 'Herman', 8 => 'Dénes', 9 => 'Erhard', 10 => 'Zsolt',
                11 => 'Leó, Szaniszló', 12 => 'Gyula', 13 => 'Ida', 14 => 'Tibor', 15 => 'Anasztázia, Tas',
                16 => 'Csongor', 17 => 'Rudolf', 18 => 'Andrea, Ilma', 19 => 'Emma', 20 => 'Tivadar',
                21 => 'Konrád', 22 => 'Csilla, Noémi', 23 => 'Béla', 24 => 'György', 25 => 'Márk',
                26 => 'Ervin', 27 => 'Zita', 28 => 'Valéria', 29 => 'Péter', 30 => 'Katalin, Kitti'
            ],
            5 => [
                1 => 'Fülöp, Jakab', 2 => 'Zsigmond', 3 => 'Tímea, Irma', 4 => 'Mónika, Flórián', 5 => 'Györgyi',
                6 => 'Ivett, Frida', 7 => 'Gizella', 8 => 'Mihály', 9 => 'Gergely', 10 => 'Ármin, Pálmira',
                11 => 'Ferenc', 12 => 'Pongrác', 13 => 'Szervác, Imelda', 14 => 'Bonifác', 15 => 'Zsófia, Szonja',
                16 => 'Mózes, Botond', 17 => 'Paszkál', 18 => 'Erik, Alexandra', 19 => 'Ivó, Milán', 20 => 'Bernát, Felícia',
                21 => 'Konstantin', 22 => 'Júlia, Rita', 23 => 'Dezső', 24 => 'Eszter, Eliza', 25 => 'Orbán',
                26 => 'Fülöp, Evelin', 27 => 'Hella', 28 => 'Emil, Csanád', 29 => 'Magdolna', 30 => 'Janka, Zsanett',
                31 => 'Angéla, Petronella'
            ],
            6 => [
                1 => 'Tünde', 2 => 'Kármen, Anita', 3 => 'Klotild, Cecília', 4 => 'Bulcsú', 5 => 'Fatime, Fatima',
                6 => 'Norbert, Cintia', 7 => 'Róbert', 8 => 'Medárd', 9 => 'Félix', 10 => 'Margit, Gréta',
                11 => 'Barnabás', 12 => 'Villő', 13 => 'Antal, Anett', 14 => 'Vazul', 15 => 'Jolán, Vid',
                16 => 'Jusztin', 17 => 'Laura, Alida', 18 => 'Arnold, Levente', 19 => 'Gyárfás', 20 => 'Rafael',
                21 => 'Alajos, Leila', 22 => 'Paulina', 23 => 'Zoltán', 24 => 'Iván', 25 => 'Vilmos',
                26 => 'János, Pál', 27 => 'László', 28 => 'Levente, Irén', 29 => 'Péter, Pál', 30 => 'Pál'
            ],
            7 => [
                1 => 'Tihamér, Annamária', 2 => 'Ottó', 3 => 'Kornél, Soma', 4 => 'Ulrik', 5 => 'Emese, Sarolta',
                6 => 'Csaba', 7 => 'Apollónia', 8 => 'Ellák', 9 => 'Lukrécia', 10 => 'Amália',
                11 => 'Nóra, Lili', 12 => 'Izabella, Dalma', 13 => 'Jenő', 14 => 'Örs, Stella', 15 => 'Henrik, Roland',
                16 => 'Vali', 17 => 'Endre, Elek', 18 => 'Frigyes', 19 => 'Emília', 20 => 'Illés',
                21 => 'Dániel, Daniella', 22 => 'Magdolna', 23 => 'Lenke', 24 => 'Kinga, Kincső', 25 => 'Kristóf, Jakab',
                26 => 'Anna, Anikó', 27 => 'Olga, Liliána', 28 => 'Szabolcs', 29 => 'Márta, Flóra', 30 => 'Judit, Xénia',
                31 => 'Oszkár'
            ],
            8 => [
                1 => 'Boglárka', 2 => 'Lehel', 3 => 'Hermina', 4 => 'Domonkos, Dominika', 5 => 'Krisztina',
                6 => 'Berta, Bettina', 7 => 'Ibolya', 8 => 'László', 9 => 'Emőd', 10 => 'Lőrinc',
                11 => 'Zsuzsanna, Tiborc', 12 => 'Klára', 13 => 'Ipoly', 14 => 'Marcell', 15 => 'Mária',
                16 => 'Ábrahám', 17 => 'Jácint', 18 => 'Ilona', 19 => 'Huba', 20 => 'István',
                21 => 'Sámuel, Hajna', 22 => 'Menyhért', 23 => 'Bence', 24 => 'Bertalan', 25 => 'Lajos, Patrícia',
                26 => 'Izsó', 27 => 'Gáspár', 28 => 'Ágoston', 29 => 'Beatrix, Erna', 30 => 'Rózsa',
                31 => 'Erika, Bella'
            ],
            9 => [
                1 => 'Egyed, Egon', 2 => 'Rezső', 3 => 'Hilda', 4 => 'Rozália', 5 => 'Viktor, Lőrinc',
                6 => 'Zakariás', 7 => 'Regina', 8 => 'Mária, Adrienn', 9 => 'Ádám', 10 => 'Nikolett, Hunor',
                11 => 'Teodóra', 12 => 'Mária', 13 => 'Kornél', 14 => 'Szeréna, Roxána', 15 => 'Enikő, Melitta',
                16 => 'Edit', 17 => 'Zsófia', 18 => 'Diána', 19 => 'Vilhelmina', 20 => 'Friderika',
                21 => 'Máté, Mirella', 22 => 'Mór, Gellért', 23 => 'Tekla, Líviusz', 24 => 'Gellért, Mercédesz', 25 => 'Eufrozina, Kende',
                26 => 'Jusztina', 27 => 'Adalbert', 28 => 'Vencel', 29 => 'Mihály', 30 => 'Jeromos'
            ],
            10 => [
                1 => 'Malvin', 2 => 'Petra', 3 => 'Helga', 4 => 'Ferenc', 5 => 'Aurél',
                6 => 'Brúnó, Renáta', 7 => 'Amália', 8 => 'Koppány', 9 => 'Dénes', 10 => 'Gedeon',
                11 => 'Brigitta', 12 => 'Miksa', 13 => 'Kálmán, Ede', 14 => 'Helén', 15 => 'Teréz',
                16 => 'Gál', 17 => 'Hedvig', 18 => 'Lukács', 19 => 'Nándor', 20 => 'Vendel',
                21 => 'Orsolya', 22 => 'Előd', 23 => 'Gyöngyi', 24 => 'Salamon', 25 => 'Blanka, Bianca',
                26 => 'Dömötör', 27 => 'Szabina', 28 => 'Simon, Szimonetta', 29 => 'Nárcisz', 30 => 'Alfonz',
                31 => 'Farkas'
            ],
            11 => [
                1 => 'Marianna', 2 => 'Achilles', 3 => 'Győző', 4 => 'Károly', 5 => 'Imre',
                6 => 'Lénárd', 7 => 'Rezső', 8 => 'Zsombor', 9 => 'Tivadar', 10 => 'Réka',
                11 => 'Márton', 12 => 'Jónás, Renátó', 13 => 'Szilvia', 14 => 'Aliz', 15 => 'Albert, Lipót',
                16 => 'Ödön', 17 => 'Hortenzia, Gergő', 18 => 'Jenő', 19 => 'Erzsébet', 20 => 'Jolán',
                21 => 'Olivér', 22 => 'Cecília', 23 => 'Klementina', 24 => 'Emma', 25 => 'Katalin',
                26 => 'Virág', 27 => 'Virgil', 28 => 'Stefánia', 29 => 'Taksony', 30 => 'András, Andor'
            ],
            12 => [
                1 => 'Elza', 2 => 'Melinda, Vivien', 3 => 'Ferenc, Olívia', 4 => 'Borbála, Barbara', 5 => 'Vilma',
                6 => 'Miklós', 7 => 'Ambrus', 8 => 'Mária', 9 => 'Natália', 10 => 'Judit',
                11 => 'Árpád', 12 => 'Gabriella', 13 => 'Luca, Otília', 14 => 'Szilárda', 15 => 'Valér',
                16 => 'Etelka, Aletta', 17 => 'Lázár, Olimpia', 18 => 'Auguszta', 19 => 'Viola', 20 => 'Teofil',
                21 => 'Tamás', 22 => 'Zénó', 23 => 'Viktória', 24 => 'Ádám, Éva', 25 => 'Eugénia',
                26 => 'István', 27 => 'János', 28 => 'Kamilla', 29 => 'Tamás, Szeréna', 30 => 'Dávid',
                31 => 'Szilveszter'
            ]
        ];
    
        public function __construct() {
            add_shortcode('nevnapok', [$this, 'render_shortcode']);
        }
    
        /**
         * Névnap lekérése adott dátumra (kezeli a szökőéveket)
         */
        private function get_nevnap_by_timestamp($timestamp) {
            $month = (int)date('n', $timestamp);
            $day = (int)date('j', $timestamp);
            $is_leap_year = (date('L', $timestamp) == 1);
    
            // Szökőév esetén február 24. után a névnapok 1 nappal eltolódnak
            if ($is_leap_year && $month == 2) {
                if ($day == 24) {
                    return 'Szökőnap (Nincs külön névnap)';
                } elseif ($day > 24) {
                    $day--; // Február 25-29. a 24-28-ai névnapokat kapja
                }
            }
    
            return isset(self::$nevnapok[$month][$day]) ? self::$nevnapok[$month][$day] : 'Ismeretlen';
        }
    
        /**
         * Shortcode megjelenítése: [nevnapok]
         */
        public function render_shortcode($atts) {
            // Alapértelmezett beállítások felülírhatósága
            $atts = shortcode_atts([
                'mai_label'     => 'Mai névnap:',
                'holnapi_label' => 'Holnapi névnap:',
                'layout'        => 'block' // 'block' vagy 'inline'
            ], $atts, 'nevnapok');
    
            // WordPress időzóna beállítása alapján kérjük le az időt
            $now = current_time('timestamp');
            $tomorrow = strtotime('+1 day', $now);
    
            $mai_nevnap = $this->get_nevnap_by_timestamp($now);
            $holnapi_nevnap = $this->get_nevnap_by_timestamp($tomorrow);
    
            // HTML kimenet előállítása
            ob_start();
            ?>
            <div class="magyar-nevnapok-container layout-<?php echo esc_attr($atts['layout']); ?>">
                <div class="nevnap-item nevnap-mai">
                    <span class="nevnap-label"><?php echo esc_html($atts['mai_label']); ?></span>
                    <span class="nevnap-name"><?php echo esc_html($mai_nevnap); ?></span>
                </div>
                <div class="nevnap-item nevnap-holnapi">
                    <span class="nevnap-label"><?php echo esc_html($atts['holnapi_label']); ?></span>
                    <span class="nevnap-name"><?php echo esc_html($holnapi_nevnap); ?></span>
                </div>
            </div>
            <style>
                .magyar-nevnapok-container.layout-inline {
                    display: flex;
                    gap: 15px;
                    align-items: center;
                }
                .magyar-nevnapok-container .nevnap-label {
                    font-weight: bold;
                    margin-right: 4px;
                }
            </style>
            <?php
            return ob_get_clean();
        }
    }
    
    // Bővítmény inicializálása
    new Magyar_Nevnapok_Plugin();
    

    Használati útmutató

    1. Telepítés:
      • Hozz létre egy magyar-nevnapok nevű mappát a weboldalad wp-content/plugins/ könyvtárában.
      • Mentsd el a fenti kódot magyar-nevnapok.php néven ebbe a mappába.
      • Lépj be a WordPress admin felületére, és kapcsold be a Magyar Névnapok (Mai és Holnapi) bővítményt.
    2. Megjelenítés Shortcode-csal:Bármelyik oldalra, bejegyzésbe, widgetsávba vagy fejlécbe beillesztheted az alábbi shortcode-ok egyikét:
      • Alapértelmezett (blokk elrendezés):[ nevnapok ]
      • Egymás melletti (egysoros) elrendezés:[ nevnapok layout="inline" ]
      • Egyedi feliratok használata:[ nevnapok mai_label="Ma:" holnapi_label="Holnap:" layout="inline" ]

    1.Fájl létrehozása:

    Hozd létre a magyar-nevnapok.php fájlt a wp-content/plugins/magyar-nevnapok/ könyvtárban.

    2.Aktiválás:

    Lépj be a WordPress Adminba -> Bővítmények, majd aktiváld a „Magyar Névnapok” nevű bővítményt.

    3.Shortcode elhelyezése:

    Szúrd be a [ nevnapok ] shortcode-ot tetszőleges oldalba, blokkba vagy widgetbe.

  • Ingyen kávé az irodában

    Ingyen kávé az irodában

    Minap volt ez a cikk a Telexen, ami nem kis indulatot váltott ki belőlem. Sose felejtem el az első munkahelyeimet, ahol semmi sem volt, még a bér is fekete vagy épp szürke zónás, de azon kívül semmi. Aztán szépen sikerült előre lépnem szakmailag és cég tekintetében, és bár életem igen nagy részében multinál dolgoztam, az ingyenes kávé alap volt ezeken a helyeken. Volt, hol jobb, máshol kevésbé jobb minőségben, de volt. Aztán volt olyan cég is, ahol a falból folyt a kóla. Ilyen is van. Ezzel szemben nagyon nehéz megérteni azon munkáltatókat, akik erre egyáltalán nem áldoznak.

    Ez szerintem egy olyan alap gesztus kéne, hogy legyen, ami arra hivatott, hogy jól érezd a bent töltött időt, attól nem beszélve, hogy az IT pont az a szakma, ahol igen magasan reprezentált a kávéfogyasztás, ami valószínűleg betudható annak a pörgésnek, és túlmunkának, ami ezzel jár.

    Szóval privát véleményem, hogy lehet erre, mint az otthoni munkavégzés megszüntetésére érveket keresni, csak épp nincs értelme, mert a valóság szembe megy ezekkel.

    Más részről pedig egy barátomtól hallott mondás jut eszembe, „ki banánnal fizet, az majmot kap”.

  • POM avagy Page Object Model

    Mire jó az AI? Hát például arra, hogy ha írni szeretnék egy témáról, de lusta vagyok, az egyébként is Google segítsével összeszedett információkat rendszerbe szedni, akkor relatív gyorsan kézhez kapok egy bejegyzést.

    A Page Object Model (POM) a szoftvertesztelés-automatizálás egyik legnépszerűbb és leghatékonyabb tervezési mintája (Design Pattern). A lényege, hogy az alkalmazás egyes weboldalait (vagy oldalrészeit) különálló objektumokként képezi le a kód szintjén.

    Miért érdemes használni?

    • Karbantarthatóság: Ha megváltozik egy gomb ID-ja vagy egy mező elhelyezkedése, elegendő azt egyetlen helyen (a Page Object osztályban) átírni, a teszteseteket nem kell bántani.
    • Kód újrafelhasználhatósága: A gyakori műveletek (pl. bejelentkezés, keresés) egyszer vannak megírva, és bármelyik tesztből meghívhatók.
    • Olvashatóság: A tesztkód tiszta, átlátható és üzleti logikát követő lesz, rejtve a techniai megvalósítást (pl. DOM-lekérdezéseket).

    Minta megvalósítás (Python + Selenium)

    Az alábbiakban egy klasszikus bejelentkezési folyamat példáján keresztül látható a POM működése.

    A Page Object osztály (login_page.py)

    Ez az osztály tartalmazza az oldal elemeinek azonosítóit (lokátorait) és a végezhető műveleteket.

    Python

    from selenium.webdriver.common.by import By
    
    class LoginPage:
        def __init__(self, driver):
            self.driver = driver
            
            # Lokátorok (különválasztva a logikától)
            self._username_input = (By.ID, "username")
            self._password_input = (By.ID, "password")
            self._login_button = (By.CSS_SELECTOR, "button[type='submit']")
            self._error_message = (By.CLASS_NAME, "flash-error")
    
        # Akciók / Műveletek
        def enter_username(self, username: str):
            self.driver.find_element(*self._username_input).send_keys(username)
    
        def enter_password(self, password: str):
            self.driver.find_element(*self._password_input).send_keys(password)
    
        def click_login(self):
            self.driver.find_element(*self._login_button).click()
    
        # Összetett folyamat (Láncolt / Magasabb szintű akció)
        def login(self, username: str, password: str):
            self.enter_username(username)
            self.enter_password(password)
            self.click_login()
    
        def get_error_message(self) -> str:
            return self.driver.find_element(*self._error_message).text
    

    A Teszteset (test_login.py)

    A tesztkód csak a Page Object által nyújtott metódusokat használja, így végtelenül egyszerű és olvasható.

    Python

    import pytest
    from selenium import webdriver
    from login_page import LoginPage
    
    @pytest.fixture
    def driver():
        driver = webdriver.Chrome()
        driver.get("https://example.com/login")
        yield driver
        driver.quit()
    
    def test_successful_login(driver):
        login_page = LoginPage(driver)
        
        # Bejelentkezés végrehajtása
        login_page.login("valid_user", "correct_password")
        
        # Ellenőrzés (Assertion)
        assert driver.current_url == "https://example.com/dashboard"
    
    def test_failed_login(driver):
        login_page = LoginPage(driver)
        
        login_page.login("invalid_user", "wrong_password")
        
        # Hibaüzenet ellenőrzése
        assert "Érvénytelen adatok" in login_page.get_error_message()
    

    Jó tanácsok a gyakorlati alkalmazáshoz

    • Ne tegyél assertion-öket a Page Object-be: A assert kifejezések a tesztfájlban maradjanak, a Page Object csak adatot adjon vissza vagy állapotot módosítson.
    • Page Factory használata: Bizonyos keretrendszerekben (pl. Java + Selenium) a @FindBy annotációkkal még tisztább szerkezet érhető el.
    • Lazy loading: Az elemeket csak akkor kérd le a DOM-ból, amikor ténylegesen szükség van rájuk (például dinamikus felületek esetén).

    A Page Object Model (POM) megvalósítása a modernebb eszközökkel (mint a Playwright vagy a Selenium Java-alapú változata) még letisztultabbá válik, mivel ezek a keretrendszerek beépített architektúrával támogatják a megbízhatóbb elemkezelést.

    Az alábbiakban mindkét megközelítésre találsz egy-egy komplett kódmintát.

    Playwright + TypeScript (Ajánlott / Modern megközelítés)

    A Playwright eleve magában foglalja az automatikus várakozást (Auto-waiting), így nincs szükség explicit wait-ek kézi kezelésére.

    A Page Object osztály (pages/LoginPage.ts)

    TypeScript

    import { Page, Locator } from '@playwright/test';
    
    export class LoginPage {
      readonly page: Page;
      readonly usernameInput: Locator;
      readonly passwordInput: Locator;
      readonly loginButton: Locator;
      readonly errorMessage: Locator;
    
      constructor(page: Page) {
        self.page = page;
        // A Playwright-ban a lokátorok deklaratívak, nem kérdezik le azonnal a DOM-ot
        this.usernameInput = page.locator('#username');
        this.passwordInput = page.locator('#password');
        this.loginButton = page.locator('button[type="submit"]');
        this.errorMessage = page.locator('.flash-error');
      }
    
      async goto() {
        await this.page.goto('https://example.com/login');
      }
    
      async login(username: string, password: string) {
        await this.usernameInput.fill(username);
        await this.passwordInput.fill(password);
        await this.loginButton.click();
      }
    
      async getErrorMessage(): Promise<string> {
        return await this.errorMessage.textContent() || '';
      }
    }
    

    A Tesztfájl (tests/login.spec.ts)

    TypeScript

    import { test, expect } from '@playwright/test';
    import { LoginPage } from '../pages/LoginPage';
    
    test.describe('Bejelentkezés tesztek', () => {
      let loginPage: LoginPage;
    
      test.beforeEach(async ({ page }) => {
        loginPage = new LoginPage(page);
        await loginPage.goto();
      });
    
      test('Sikeres bejelentkezés', async ({ page }) => {
        await loginPage.login('valid_user', 'correct_password');
        
        // Ellenőrzés Playwright assert-tel
        await expect(page).toHaveURL('https://example.com/dashboard');
      });
    
      test('Hibás bejelentkezés', async () => {
        await loginPage.login('invalid_user', 'wrong_password');
        
        await expect(loginPage.errorMessage).toBeVisible();
        await expect(loginPage.errorMessage).toContainText('Érvénytelen adatok');
      });
    });
    

    Java + Selenium (Klasszikus keretrendszer)

    Java környezetben a PageFactory és a @FindBy annotációk használata a legelterjedtebb POM minta.

    A Page Object osztály (LoginPage.java)

    Java

    package pages;
    
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.WebElement;
    import org.openqa.selenium.support.FindBy;
    import org.openqa.selenium.support.PageFactory;
    
    public class LoginPage {
        private WebDriver driver;
    
        // Lokátorok deklarálása annotációkkal
        @FindBy(id = "username")
        private WebElement usernameInput;
    
        @FindBy(id = "password")
        private WebElement passwordInput;
    
        @FindBy(css = "button[type='submit']")
        private WebElement loginButton;
    
        @FindBy(className = "flash-error")
        private WebElement errorMessage;
    
        // Konstruktor: inicializálja a PageFactory elemeit
        public LoginPage(WebDriver driver) {
            this.driver = driver;
            PageFactory.initElements(driver, this);
        }
    
        public void enterUsername(String username) {
            usernameInput.clear();
            usernameInput.sendKeys(username);
        }
    
        public void enterPassword(String password) {
            passwordInput.clear();
            passwordInput.sendKeys(password);
        }
    
        public void clickLogin() {
            loginButton.click();
        }
    
        public void login(String username, String password) {
            enterUsername(username);
            enterPassword(password);
            clickLogin();
        }
    
        public String getErrorMessageText() {
            return errorMessage.getText();
        }
    }
    

    A Tesztosztály (JUnit 5 használatával) (LoginTest.java)

    Java

    package tests;
    
    import org.junit.jupiter.api.AfterEach;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.Test;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.chrome.ChromeDriver;
    import pages.LoginPage;
    
    import static org.junit.jupiter.api.Assertions.assertEquals;
    import static org.junit.jupiter.api.Assertions.assertTrue;
    
    public class LoginTest {
        private WebDriver driver;
        private LoginPage loginPage;
    
        @BeforeEach
        public void setUp() {
            driver = new ChromeDriver();
            driver.manage().window().maximize();
            driver.get("https://example.com/login");
            loginPage = new LoginPage(driver);
        }
    
        @Test
        public void testSuccessfulLogin() {
            loginPage.login("valid_user", "correct_password");
            assertEquals("https://example.com/dashboard", driver.getCurrentUrl());
        }
    
        @Test
        public void testFailedLogin() {
            loginPage.login("invalid_user", "wrong_password");
            assertTrue(loginPage.getErrorMessageText().contains("Érvénytelen adatok"));
        }
    
        @AfterEach
        public void tearDown() {
            if (driver != null) {
                driver.quit();
            }
        }
    }
    

    Főbb különbségek a két megközelítés között

    SzempontPlaywright (TypeScript)Selenium (Java)
    LokátorokA Locator objektumok deklaratívak (auto-wait)A @FindBy elemek lekezeléséhez várakozás (WebDriverWait) kellhet
    PárhuzamosításAlapból támogatott a tesztfutztatóbanTestNG vagy JUnit szálkezelést igényel
    InicializálásEgyszerű objektumkonstruktorPageFactory.initElements() hívás szükséges

    A Playwright Custom Fixture funkciója segítségével a Page Object osztályok példányosítását teljesen kiszervezheted a tesztekből. Ezzel elkerülhető, hogy minden egyes tesztfájlban manuálisan kelljen megadni a const loginPage = new LoginPage(page) sorokat.

    A tesztek így közvetlenül paraméterként kérhetik el a szükséges oldalobjektumokat, az infrastruktúra pedig automatikusan példányosítja és előkészíti azokat.

    A Page Object osztályok (pages/)

    A megszokott módon hozzuk létre az oldalklasszokat.

    TypeScript

    // pages/LoginPage.ts
    import { Page, Locator } from '@playwright/test';
    
    export class LoginPage {
      readonly page: Page;
      readonly usernameInput: Locator;
    
      constructor(page: Page) {
        this.page = page;
        this.usernameInput = page.locator('#username');
      }
    
      async goto() {
        await this.page.goto('/login');
      }
    }
    
    // pages/DashboardPage.ts
    import { Page, Locator } from '@playwright/test';
    
    export class DashboardPage {
      readonly page: Page;
      readonly userMenu: Locator;
    
      constructor(page: Page) {
        this.page = page;
        this.userMenu = page.locator('#user-menu');
      }
    }
    

    A Custom Fixture létrehozása (fixtures/baseTest.ts)

    Itt terjesztjük ki a Playwright alapértelmezett test objektumát, megadva a saját Page Object fixture-jeinket.

    TypeScript

    import { test as base } from '@playwright/test';
    import { LoginPage } from '../pages/LoginPage';
    import { DashboardPage } from '../pages/DashboardPage';
    
    // 1. Típusdefiníció a fixture-ökhöz
    type MyFixtures = {
      loginPage: LoginPage;
      dashboardPage: DashboardPage;
    };
    
    // 2. A 'base.extend' használatával regisztráljuk a saját fixture-jeinket
    export const test = base.extend<MyFixtures>({
      // Automatikusan példányosítja a LoginPage-t és igény esetén megnyitja az oldalt
      loginPage: async ({ page }, use) => {
        const loginPage = new LoginPage(page);
        await loginPage.goto(); // Akár előkészítő lépéseket is tehetünk ide
        await use(loginPage);   // Átadja az objektumot a tesztnek
      },
    
      // Példányosítja a DashboardPage-t
      dashboardPage: async ({ page }, use) => {
        const dashboardPage = new DashboardPage(page);
        await use(dashboardPage);
      },
    });
    
    // Az 'expect'-et is exportáljuk a kényelmes importálásért
    export { expect } from '@playwright/test';
    

    A Fixture-ök használata a tesztekben (tests/login.spec.ts)

    A @playwright/test helyett a saját fixtures/baseTest fájlunkból importáljuk a test objektumot. A tesztfüggvény paramétereként megadott nevek alapján a Playwright automatikusan beadagolja a megfelelő példányokat.

    TypeScript

    // Figyelem: A saját kiterjesztett test objektumunkat importáljuk!
    import { test, expect } from '../fixtures/baseTest';
    
    test('Sikeres bejelentkezés custom fixture-rel', async ({ loginPage, dashboardPage }) => {
      // A loginPage.goto() már lefutott a fixture-ben!
      await loginPage.usernameInput.fill('standard_user');
      
      // A dashboardPage is azonnal használható, nem kellett 'new'-val példányosítani
      await expect(dashboardPage.userMenu).toBeVisible();
    });
    

    Miért ez a legnépszerűbb Playwright architektúra?

    • Nulla boilerplate kód: Nem kell beforeEach blokkokban példányosítani az osztályokat.
    • Lazy loading (Lusta betöltés): Csak azok a Page Object-ek példányosulnak, amiket a teszt fejlécében ({ loginPage, dashboardPage }) kifejezetten elkérsz.
    • Beépített tear-down: A fixture-ben a use() hívás utáni kódrészlet automatikusan lefut a teszt végén (takarításra, kijelentkezésre ideális).
  • API G – Mi volt az igény?

    „busy, aki a héten nem szállít…”

    Mit mást is mondhatnék? Aki a szakmában dolgozni, szerintem sír a röhögéstől, miközben magára ismer 🙂

  • Playwright 1.63 újdonságok

    Playwright 1.63 újdonságok

    Újdonságok

    A Playwright 1.63 frissítése megérkezett, és számtalan olyan funkciót hoz, amelyek lényegesen egyszerűbbé teszik a komplex tesztelési munkafolyamatokat, a párhuzamosítást és a hibakeresést.

    Az alábbiakban összefoglaltuk a kiadás legfontosabb újdonságait és fejlesztéseit.

    Key Features & Újdonságok

    1. Test Locks (Tesztreteszek)

    • Megoldást nyújt a közös erőforrások (pl. központi adatbázis, globális fiókbeállítások vagy külső API-k) használata miatti ütközésekre.
    • Lehetővé teszi zárolások (locks) megadását, így a tesztek automatikusan sorba állnak a kritikus erőforrások eléréséhez, elkerülve a párhuzamos futtatásból eredő hibákat.

    2. Step paraméterek és feliratok (test.step)

    • A test.step() mostantól elfogad egy params opciót, amellyel saját metaadatokat és paramétereket adhatsz át a lépésekhez.
    • A riportokban és a Trace Viewerben tisztábban nyomon követhetők az egyedi lépésekhez tartozó változók és hívási argumentumok.

    3. Aria Traces & Bővített Tracing

    • A Trace Viewer új aria pillanatképeket (snapshots) támogat.
    • Megkönnyíti a hozzáférhetőségi (accessibility) elemek és az ARIA fa állapotának elemzését hibakeresés közben.

    4. Új Scroll Opciók az akcióknál

    • A legtöbb akciónál (pl. .click()) megjelent egy új scroll opció ("auto" | "none").
    • Lehetőséget biztosít arra, hogy kikapcsold a Playwright automatikus „scroll-into-view” viselkedését, ha egy elem pozícióját manuálisan szeretnéd kezelni.

    5. Új teszt opciók és CLI parancsok

    • Nagyobb hozzáférhetőség tesztelése: Új reducedMotion, forcedColors és contrast opciók a testOptions alatt.
    • --add-reporter parancssori opció: Bővítheted a meglévő riporterek listáját ahelyett, hogy felülírnád a playwright.config.ts-ben megadottakat.
    • omitTags opció: Letisztultabb tesztcímek a riportokban (suppress-eli az automatikusan hozzáfűzött tegeket a list, line, dot, github és junit riportereknél).

    Eltávolított (Deprecated) API-k

    A 1.63-as verzió végleg kivezeti a régen elavultnak jelölt funkciókat:

    • Locator.ariaRef() (használd helyette a locator.ariaSnapshot() funkciót).
    • handle opció a BrowserContext.exposeBinding és Page.exposeBinding esetében.
    • logger opció a BrowserType.connect és connectOverCDP metódusoknál.
    • videosPath és videoSize konfig opciók (használd a recordVideo-t).

    Frissítés

    A legújabb verzióra való átálláshoz futtasd a projekt gyökerében az alábbi parancsot:

    Bash

    npm install -D @playwright/test@latest
    npx playwright install

    Test Lock minta

    A Playwright 1.63-ban megjelent Test Locks (Tesztreteszek) opció segítségével megakadályozhatod, hogy bizonyos tesztek egyszerre futjanak és ütközzenek a közös erőforrásokon (pl. ugyanazon tesztfiók beállításai, adatbázis vagy külső API-k), miközben a többi független teszt továbbra is párhuzamosan fut.

    Az alábbiakban bemutatjuk a legfontosabb használati módokat kódpéldákkal.

    1. Egyedi teszt zárolása egy megnevezett retesszel (lock)

    Ha van két vagy több teszted, amelyek ugyanazt a megosztott erőforrást módosítják, megadhatsz nekik egy azonos lock nevet az opciók között:

    TypeScript

    import { test, expect } from '@playwright/test';
    
    // Ez a teszt lefoglalja az 'admin-account' reteszt
    test('Adminisztrátor frissíti a cég nevét', { lock: 'admin-account' }, async ({ page }) => {
      await page.goto('https://example.com/admin/settings');
      await page.getByLabel('Cégnév').fill('Új Cégnév Kft.');
      await page.getByRole('button', { name: 'Mentés' }).click();
    
      await expect(page.getByText('Sikeres mentés')).toBeVisible();
    });
    
    // Ez a teszt megvárja, amíg az előző teszt elengedi az 'admin-account' reteszt
    test('Adminisztrátor módosítja az időzónát', { lock: 'admin-account' }, async ({ page }) => {
      await page.goto('https://example.com/admin/settings');
      await page.getByLabel('Időzóna').selectOption('Europe/Budapest');
      await page.getByRole('button', { name: 'Mentés' }).click();
    
      await expect(page.getByText('Sikeres mentés')).toBeVisible();
    });
    

    Hogyan működik? Amíg a Worker 1 az első tesztet futtatja az admin-account retesszel, addig a Worker 2 nem kezdi el a második tesztet. A két teszt egymás után fog lefutni, de a csomag többi, független tesztje (pl. termékkeresés, kosárkezelés) továbbra is teljesen párhuzamosan fut a többi workeren.

    2. Egész tesztcsoport zárolása (test.describe)

    Ha egy egész blokknyi teszt egyetlen közös erőforráson dolgozik, a lock opciót közvetlenül a test.describe() csoportra is ráteheted:

    TypeScript

    import { test, expect } from '@playwright/test';
    
    test.describe('Adatbázis konfigurációs tesztek', { lock: 'database-config' }, () => {
      
      test('Alapértelmezett nyelv beállítása', async ({ page }) => {
        // ... teszt kód
      });
    
      test('Rendszerértesítések kikapcsolása', async ({ page }) => {
        // ... teszt kód
      });
    
    });
    

    3. Több retesz egyidejű használata (Array)

    Egy teszt akár több megosztott erőforrás zárolását is kérheti egyszerre. Ehhez adj meg egy tömböt a lock paraméterben:

    TypeScript

    import { test, expect } from '@playwright/test';
    
    test(
      'Fizetési átjáró és szállítási beállítások egyidejű frissítése',
      { lock: ['payment-sandbox', 'shipping-api'] },
      async ({ page }) => {
        // Ez a teszt csak akkor indul el, ha mindkét retesz szabad
        await page.goto('https://example.com/admin/integrations');
        // ... teszt kód
      }
    );
  • Automatizáló framework-ök a szakmám során

    Eddigi pályafutásom során jó pár framework-höz volt szerencsém különféle programnyelvek mellett, és hát azt kell mondanom, hogy ideje ezeket összegyűjtenem, mert kezdem egy részét elfelejteni.

    • Selenium WebDriver (#C, JAVA)
    • HP UFT (Visual Basic Script)
    • Katalon Studio (Kotlin)
    • Cypress (JavaScript)
    • Playwright (TypeScript)

    Selenium + C# + NUnit

    Kezdjünk is bele. Szóval még az automatizáló pályám elején volt lehetőségem a munkám mellett gyakorolni, és az épp aktuális Selenium volt a választott C#-pal. Ez akkoriban azt jelentette, hogy ahogy ma egy demó kód kinéz, kb úgy írtam én is meg Visual Studioban, majd pedig NUnitban a saját gépemről futottak a tesztek. Attól függetlenül, hogy messze volt egy professziónális megoldástól, szépen fogta a regressziós hibákat.

    Valahogy így kell elképzelni, ahogy a tesztek futottak.

    HP UFT + Visual Basic Script + HP ALM

    Ez volt az első hivatalos automatizálós projektem, ami előtt még kaptam HP képzést is, bár ennél a munka és a szükséges tudás összeszedése internetről sokkal hasznosabbnak bizonyult. Ahogy nézem, a HP dolgai igencsak megváltoztak, más kezeli őket.

    HP ALM kapcsán a Gurunál találtam egy értékelhető leírást, és a jól emlékszem, én ennek az oldalnak használtam fel a tudásanyagát annak idején. Viszont jól látszik, hogy elszaladt az idő felette. Talán az egyik cimborám kérdezte pár éve, hogy mi a véleményem egy ilyen projektről, és már akkor azt mondtam, hogy kerülje el, mert nincs jövője.

    Annak idején ez az ökoszisztéma igencsak jól működött, bár ára is volt (cég fizette). Az ALM végezte a központosítást, az UFT pedig az automata tesztek fejlesztésért volt felelős.

    Így nézett ki az UFT.

    Az ALM pedig így.

    Szerintem ez a kettő együtt akkoriban zseniális párost alkotott.

    Katalon Studio

    Kísérletezgettem vele annak idején. Még tetszett is, de valahogy körülményesnek ítéltem meg a használatát. Ennél egy IDEs kódszerkesztővel jobban és minőségibben lehet/lehetett dolgozni. Az pozitív volt, hogy próbálta egy IDE-n belül kezelni a dolgokat, mint a HP UFT, viszont egy idő után kezdett átláthatatlanná válni számomra. Azóta egyébként valószínűleg ez is sokat fejlődött, bár nekem nem volt időm követni a változásokat.

    Valami ilyesmire emlékszem.

    Cypress + JavaScript

    Na ezt imádtam. Könnyedén és gyorsan lehetett benne dolgozni, bár csak 1 évet töltöttem vele. Egy negatívuma azért van/volt, hogy az async/await dolgokat maga kezeli. Ez az esetek túlnyomó többségében nagyon jól működik, de amikor valamiért ez nem jó, az fájdalmas tud lenni.

    Ha jól rémlik, én is VS Code-dal használtam, illetve GitLab-ban futottak a tesztek.

    Ha jól sejtem VS Codeban.

    Playwright + TypeScript

    Abszolút kedvenc. 3 évet töltöttem vele, és imádtam minden percet. Annyira könnyed és légies, és gyorsan lehet vele haladni annak köszönhetően, hogy zseniálisan van felépítve benne szinte minden, hogy csak ajánlani tudom.

    Több különböző nyelvet is támogat, bár nekem a TypeScript volt használatban. Illetve nagyon jól dokumentált. Nem volt szükség sok Google keresésre, mivel a leírás hozzá megválaszolt mindent.

    Ez pedig ilyen volt.

  • Manuális tesztelés, és/vagy automatizáció

    Amikor arról beszélünk, hogy mi a különbség a manuális és az automatizált tesztelés között, akkor nagyok sokan, nagyon sokféle választ adnak erre a kérdésre egy interjú során, majd pedig ezen meglátások többé-kevésbé mégsem valósulnak meg. Ennek oka többféle lehet. Például nem minden iparágban lehet ugyanazokat a szabályokat alkalmazni, vagy épp az üzleti érdek, kapacitás és/vagy tesztelésre fordítható anyagi javak ezt felülbírálják.

    Én meg azok köze tartozom, akik abban szocializálódtak, hogy a manuális tesztelés az a folyamat, ami az adott sprinten belül vizsgálja az új fejlesztést, és annak hatását az egész rendszerre. Ehhez persze érteni kell magát, az új fejlesztést, illetve, hogy mi módon kapcsolódik bele a már meglévő szisztémába. Ehhez normális esetben társulnak elfogadási kritériumok is, melyek a főbb csapásvonalak és működés leírását és elfogadását határozzák meg.

    Miután ez megvan, és az adott fejlesztés átment a minőségbiztosítás ezen folyamatán, utána következik az, ahol megvizsgálásra kerül, hogy mely tesztesetek szükségesek a regressziós folyamat ellátására. Azok kiválasztásra, majd pedig automatizálásra kerülnek, ezzel elérve azt a célt, hogy minden regressziós teszt futtatás során minimális emberi beavatkozással kaphassunk egy eredmény, ami igazolja, vagy épp cáfolja, hogy a rendszerünk az új fejlesztés után is megfelelően működik.

    Manuális tesztelésAutomatizálás
    üzleti logika teszteléseregresszió tesztek megvalósítása
    követelmény alapú megvalósításidő spórolás, ismétlődő teszt esetek
    bugok korai megtalálása, még az automatizálás előttgyors visszajelzés
    Exploratory és Ad-hoc tesztelésstabilan futó tesztek
    Manuális tesztelés

    A manuális tesztelés a szoftverek manuális tesztelésének folyamata a hibák feltárására. A tesztelőnek a végfelhasználó szerepét kell játszania, és az alkalmazás legtöbb funkcióját kell használnia a helyes viselkedés biztosítása érdekében. A tesztelés teljességének garantálása érdekében a tesztelő gyakran követ egy írásos teszttervet, amely végigvezeti a fontos teszteseteken.

    Áttekintés

    A folyamat egyik legfontosabb lépése a szoftver helyes viselkedésének tesztelése a végfelhasználók számára történő kiadást megelőzően.

    Kis léptékű mérnöki erőfeszítéseknél (beleértve a prototípusokat) elegendő lehet a feltáró tesztelés. Ennél az informális megközelítésnél a tesztelő nem követ szigorú tesztelési eljárást, hanem inkább az alkalmazás felhasználói felületét vizsgálja meg a lehető legtöbb funkciót felhasználva, és a korábbi tesztek során szerzett információkat felhasználva intuitív módon további teszteket vezet le. A feltáró manuális tesztelés sikere nagymértékben függ a tesztelő szakértelmétől, mivel a tudás hiánya a tesztelés hiányosságaihoz vezet. Az informális megközelítés egyik legfontosabb előnye, hogy intuitív betekintést nyerhetünk abba, milyen érzés használni az alkalmazást.

    A manuális szoftvertesztelésre támaszkodó nagyméretű mérnöki projektek szigorúbb módszertant követnek, hogy maximalizálják a fellelhető hibák számát. A szisztematikus megközelítés előre meghatározott tesztesetekre összpontosít, és általában a következő lépéseket foglalja magában:[1]

    • Kiválaszt egy magas szintű tesztelési tervet, amelyben kiválasztják az általános módszertant, meghatározzák és beszerzik az erőforrásokat, például az embereket, a számítógépeket és a szoftverlicenceket.
    • Részletes tesztesetek írása, a tesztelő által végrehajtandó világos és tömör lépések azonosítása, a várt eredményekkel együtt.
    • A tesztesetek kiosztása a tesztelőknek, akik manuálisan követik a lépéseket és rögzítik az eredményeket.
    • A tesztelők megállapításait részletező tesztelési jelentés készítése. A jelentést a menedzserek arra használják, hogy eldöntsék, kiadható-e a szoftver, és ha nem, akkor a mérnökök arra, hogy azonosítsák és kijavítsák a problémákat.

    A szigorú, teszteseteken alapuló megközelítés gyakran hagyományos a vízesésmodellt[2] követő nagy szoftverfejlesztési projektek esetében. Legalább egy nemrégiben készült tanulmány azonban nem mutatott drámai különbséget a hibák felderítésének hatékonyságában a feltáró tesztelés és a teszteset-alapú tesztelés között.[3]

    A tesztelés történhet fekete-, fehér– vagy szürkedobozos teszteléssel. A fehérdobozos tesztelés során a tesztelő a forráskódon keresztül az utasítások végrehajtásával foglalkozik. A feketedobozos tesztelés során a szoftver futtatása a hibák ellenőrzése céljából történik, és kevésbé foglalkozik azzal, hogy a bemenet feldolgozása hogyan történik. A fekete dobozos tesztelők nem férnek hozzá a forráskódhoz. A szürke dobozos tesztelés a szoftver futtatásával foglalkozik, miközben ismeri a forráskódot és az algoritmusokat.

    Statikus és dinamikus tesztelési megközelítés is alkalmazható. A dinamikus tesztelés a szoftver futtatását foglalja magában. A statikus tesztelés magában foglalja a követelmények, a kód szintaxisának ellenőrzését és minden más olyan tevékenységet, amely nem foglalja magában a program kódjának tényleges futtatását.

    A tesztelés tovább osztható funkcionális és nemfunkcionális tesztelésre. A funkcionális tesztelés során a tesztelő ellenőrzi a számításokat, az oldalon lévő bármely linket vagy bármely más mezőt, amely adott bemenet esetén kimenetet várhat. A nem funkcionális tesztelés többek között a tesztelt rendszer teljesítményének, kompatibilitásának és alkalmasságának, biztonságának és használhatóságának vizsgálatát foglalja magában.

    A kézi tesztelés előnyei

    • Alacsony költségű működés, mivel nem használnak szoftvereszközöket
    • A legtöbb hibát kézi teszteléssel meg lehet találni
    • Az emberek jobban figyelnek és ítélnek, mint az automatizált eszközök

    Összehasonlítás az automatizált teszteléssel

    A teszt automatizálás képes lehet csökkenteni vagy megszüntetni a tényleges tesztelés költségeit. A számítógép gyorsabban képes követni a rutinszerű lépéssorozatot, mint egy ember, és képes a teszteket éjszakára lefuttatni, hogy reggelre bemutassa az eredményeket. A tényleges teszteléssel megtakarított munkaerőt azonban a tesztprogram felügyeletével kell tölteni. A tesztelendő alkalmazás típusától és a választott automatizálási eszközöktől függően ez több munkát igényelhet, mint a manuális megközelítés. Ezenkívül egyes tesztelési eszközök nagyon nagy mennyiségű adatot mutatnak be, ami potenciálisan időigényes feladatot jelenthet az eredmények értelmezésében.

    Az olyan dolgokat, mint az eszközillesztők és a szoftverkönyvtárak, tesztprogramok segítségével kell tesztelni. Ezenkívül a nagyszámú felhasználó tesztelését (teljesítménytesztelés és terheléses tesztelés) jellemzően szoftverben szimulálják, nem pedig a gyakorlatban végzik el.

    Ezzel szemben a gyakran változó elrendezésű grafikus felhasználói felületeket nagyon nehéz automatikusan tesztelni. Léteznek tesztelési keretrendszerek, amelyek a felhasználói felületek regressziós tesztelésére használhatók. Ezek a billentyűleütések és egérmozdulatok sorozatainak rögzítésére, majd ezek lejátszására és annak megfigyelésére épülnek, hogy a felhasználói felület minden alkalommal ugyanúgy reagál-e. Sajnos előfordulhat, hogy ezek a felvételek nem működnek megfelelően, ha egy gombot áthelyeznek vagy átcímkéznek egy későbbi kiadásban. Az automatikus regressziós tesztet is becsaphatja, ha a program kimenete jelentősen változik.

    Forrás: Wikipédia