TestForge

Otomasi QAPelajaran 4 dari 12

English

Locator yang selamat dari sebuah refactor

Role, label, dan test id — dan kenapa rantai CSS rusak setiap sprint.

14 mntOtomasi QA

Yang merusak suite Anda bukanlah aplikasinya

Tanyakan kepada siapa pun yang merawat suite UI apa yang sebenarnya memerah, dan nyaris tidak pernah itu cacat sungguhan. Yang terjadi adalah 40 pengujian gagal karena seorang desainer membungkus sebuah bagian dengan satu <div> tambahan.

Itu masalah locator, dan ia biaya perawatan terbesar di otomasi browser. Tidak ada hal lain di track ini yang akan menghemat waktu Anda sebanyak membereskan yang satu ini di minggu pertama.

Kenapa selektor yang diserahkan devtools itu jebakan

Klik kanan, Copy selector, dan Chrome memberi Anda sesuatu seperti:

#root > div:nth-child(2) > div.sc-bdVaJa.kGJgTf > form > div:nth-child(3) > button

Setiap satu hal dalam string itu adalah detail implementasi tanpa janji apa pun terlekat padanya. sc-bdVaJa adalah kelas hasil pembangkitan yang berubah ketika stylesheet-nya dibangun ulang. nth-child(3) rusak ketika ada yang menambahkan kolom di atasnya. Penyarangannya rusak oleh perubahan tata letak apa pun.

Sebuah locator adalah kontrak antara pengujian Anda dan aplikasinya, dan pertanyaan yang perlu diajukan atas setiap locator yang Anda tulis adalah: janji apa yang saya andalkan, dan apakah tim akan menganggap melanggarnya sebagai bug? "Tombol sign-in bisa dijangkau dan berlabel Sign in" adalah janji yang akan ditepati sebuah tim. "Tombolnya adalah anak ketiga sebuah div" bukan janji yang dibuat siapa pun.

Urutan yang dijangkau

Locator bawaan Playwright, yang terbaik lebih dulu:

page.getByRole("button", { name: "Sign in" });   // 1. role + nama yang bisa diakses
page.getByLabel("Email");                        // 2. kolom form, lewat label
page.getByPlaceholder("Search cases");           // 3. ketika tidak ada label
page.getByText("No results found");              // 4. konten statis
page.getByTestId("case-row");                    // 5. pintu darurat yang eksplisit
page.locator("css=.btn-primary");                // 6. pilihan terakhir

getByRole lebih dulu, dan bukan cuma karena ia stabil. Ia menemukan elemennya dengan cara teknologi bantu menemukannya, jadi tombol yang berhenti bisa dijangkau lewat role punya cacat aksesibilitas — dan pengujian Anda gagal karenanya. Itu strategi locator yang diam-diam membelikan Anda kelas bug kedua secara cuma-cuma. Itu juga sebabnya pelajaran aksesibilitas di track manual berada di hulu pelajaran ini: role dan nama yang bisa diakses adalah konsep yang sama di sana dan di sini.

Role yang lazim akan Anda pakai: button, link, textbox, checkbox, combobox, heading, dialog, row, cell, alert.

page.getByRole("heading", { name: "Dashboard", level: 1 });
page.getByRole("link", { name: "Create project" });
page.getByRole("textbox", { name: "Search" });
page.getByRole("row", { name: /TC-12/ });

Nama dicocokkan tanpa membedakan huruf besar-kecil, dan sebagai substring hanya kalau Anda memintanya — { name: "Save", exact: true } ketika "Save" dan "Save and close" sama-sama ada di halaman itu.

Test id: pintu darurat yang jujur

Sebagian hal tidak punya nama yang bisa diakses yang layak dipakai: sebuah baris tabel, sebuah grafik, sebuah pil status, sebuah item daftar yang hanya diidentifikasi oleh datanya. Menjangkau rantai CSS di situ adalah naluri yang keliru; menambahkan kaitan yang eksplisit adalah yang benar.

<tr data-testid="case-row" data-case-id="TC-12">
page.getByTestId("case-row").filter({ hasText: "TC-12" });

Test id adalah kontrak yang disengaja dan terlihat: ia ada semata-mata untuk pengujian, ia berada di kode sumber tempat developer bisa melihatnya, dan menghapusnya jelas-jelas perubahan yang merusak. Nama kelas tidak menjanjikan apa pun dan developer yang mengganti namanya tidak punya cara tahu suite Anda bergantung padanya.

Dua aturan yang mencegah ini merosot:

  • Mintalah, alih-alih mengakali ketiadaannya. "Bisakah kita menambahkan data-testid ke komponen barisnya?" adalah pull request dua baris dan hal yang normal untuk dibuka seorang tester.
  • Jangan menaruh test id di segala hal. Test id pada tombol yang sudah punya nama yang bisa diakses dengan sempurna tidak membeli apa pun dan justru menghilangkan pemeriksaan aksesibilitas yang tadinya Anda dapatkan cuma-cuma.

Strictness itu fitur, bukan halangan

Error: strict mode violation: getByRole('button') resolved to 3 elements

Playwright menolak bertindak ketika sebuah locator ambigu. Nalurinya adalah menambahkan .first() lalu lanjut. Tahanlah: error-nya sedang memberi tahu Anda bahwa locator Anda tidak mengidentifikasi hal yang Anda maksud, dan .first() membekukan urutan DOM hari ini ke dalam pengujiannya. Ketika tombol keempat ditambahkan di atas, Anda kini mengeklik yang keliru dan pengujiannya tetap lulus — dan itu lebih buruk daripada gagal.

Persempit dengan benar saja:

// batasi ke wilayahnya
page.getByRole("dialog", { name: "Delete suite" })
    .getByRole("button", { name: "Delete" });

// saring berdasarkan isinya
page.getByRole("row").filter({ hasText: "TC-12" })
    .getByRole("button", { name: "Run" });

// saring berdasarkan elemen anaknya
page.getByRole("listitem").filter({ has: page.getByRole("img") });

.nth(2) dan .first() sah ketika posisi memang hal yang sedang Anda uji — "baris pertama adalah run terbaru" — dan berbau busuk di tempat lain mana pun.

Locator itu malas, dan justru itu yang membuatnya bekerja

const runButton = page.getByRole("button", { name: "Run" });
await page.getByRole("button", { name: "Refresh" }).click();
await runButton.click();     // menemukan ulang elemennya, setelah refresh

Locator adalah deskripsi tentang cara menemukan sesuatu, bukan referensi ke sesuatu yang sudah ditemukan. Tidak ada yang dicari sampai Anda bertindak atau mengasersikan, dan ia dicari lagi di setiap percobaan ulang. Itulah sebabnya pengujian Playwright selamat dari re-render React yang mengganti node DOM-nya, sementara alat yang lebih tua yang memegang handle elemen akan melempar error stale-element.

Dua konsekuensi yang layak dihayati: Anda bisa dengan aman mendefinisikan locator di bagian atas sebuah pengujian bahkan sebelum halamannya ada, dan locator yang disimpan di dalam sebuah page object tidak pernah basi.

Tabel yang layak disimpan

Alih-alihTulislahKarena
.locator("div.sc-bdVaJa > button")getByRole("button", { name: "Save" })Kelas hasil pembangkitan berubah di setiap pembangunan ulang
.locator("#submit-btn-2")getByRole("button", { name: "Submit" })Id sering dibangkitkan, dan yang bernomor itu urutan yang menyamar
.locator("button").first()getByRole("dialog").getByRole("button", { name: "Delete" }).first() diam-diam mengikuti urutan DOM
.locator("//div[3]/span")getByTestId("status-pill")XPath berdasarkan posisi adalah bentuk paling rapuh yang ada
getByText("Welcome back, Ada")getByText("Welcome back") atau sebuah test idLocator yang memuat data uji rusak ketika datanya berubah

Satu tambahan: jangan taruh teks yang dimiliki pemasaran di dalam sebuah locator kecuali pengujiannya memang tentang teks itu. getByRole("button", { name: "Sign in" }) itu wajar dan bermakna; mencocokkan satu paragraf prosa halaman depan adalah pengujian yang gagal karena penyesuaian redaksi lalu menyebutnya regresi.

Internasionalisasi, sekilas

Kalau aplikasinya dikirim dalam lebih dari satu bahasa, locator berbasis nama yang bisa diakses akan terikat bahasa. Jawaban bakunya adalah memaku run pengujiannya ke satu locale di config lalu menulis locator dalam bahasa itu, dan menjangkau test id pada segelintir elemen yang teksnya memang berubah-ubah tiap run. Mengasersikan teks terjemahan adalah pekerjaan lain — itu pemeriksaan konten, dan tempatnya bersama berkas terjemahan alih-alih di suite E2E.

Di mana TestForge berperan

Ketika sebuah locator rusak, riwayat run memberi tahu Anda jenis kerusakannya. Case yang sudah lulus 60 kali lalu gagal pada build yang mengganti nama sebuah stylesheet adalah kegagalan locator; perbaikannya ada di locator-nya, dan case-nya bukan sebuah cacat. Case yang berbolak-balik antara lulus dan gagal pada build yang sama adalah hal lain, dan pelajaran tentang pengujian labil yang menanganinya.

Mencatat perbedaan itu dengan jujur — perbaikan locator sebagai perawatan, bukan sebagai bug yang ditemukan — adalah yang menjaga metrik cacat Anda tetap berarti sebagaimana yang ia katakan. Pelajaran metrik di track manual membuat poin yang sama dari sisi yang lain.

Selanjutnya: asersi dan menunggu — web-first assertion, auto-waiting, dan kenapa sleep() adalah bug alih-alih jalan pintas.

Uji pemahaman Anda

3 pertanyaan. Tidak perlu akun, dan tidak ada yang dikirim ke mana pun selain ke pemeriksa jawaban.

  1. 1. Sebuah pengujian gagal dengan "strict mode violation: resolved to 3 elements". Seorang rekan membetulkannya dengan menambahkan .first(). Apa yang keliru dengan perbaikan itu?

  2. 2. Kenapa getByRole("button", { name: "Sign in" }) cenderung menangkap kelas bug yang tidak akan pernah ditangkap selektor CSS?

  3. 3. Locator mana di antara ini yang kemungkinan butuh perawatan karena alasan yang bukan cacat?(pilih semua yang sesuai)

Jawab semua pertanyaan dulu.