Perjalanan Pengujian E2E Bagian 1: STUI ke WebdriverIO

Diterbitkan: 2019-11-21

Catatan: Ini adalah kiriman dari #frontend@twiliosendgrid. Untuk posting teknik lainnya, buka gulungan blog teknis.

Karena arsitektur frontend SendGrid mulai matang di seluruh aplikasi web kami, kami ingin menambahkan tingkat pengujian lain selain unit biasa dan lapisan pengujian integrasi. Kami berusaha membangun halaman dan fitur baru dengan cakupan pengujian E2E (end-to-end) dengan alat otomatisasi browser.

Kami ingin mengotomatiskan pengujian dari perspektif pelanggan dan untuk menghindari pengujian regresi manual jika memungkinkan untuk setiap perubahan besar yang mungkin terjadi pada bagian mana pun dari tumpukan. Kami memiliki, dan masih memiliki, tujuan berikut: menyediakan cara untuk menulis pengujian otomatisasi E2E yang konsisten, dapat di-debug, dipelihara, dan berharga untuk aplikasi frontend kami dan berintegrasi dengan CICD (integrasi berkelanjutan dan penerapan berkelanjutan).

Kami bereksperimen dengan beberapa pendekatan teknis hingga kami menyelesaikan solusi ideal kami untuk pengujian E2E. Pada tingkat tinggi, ini meringkas perjalanan kami:

  • Membangun solusi Ruby Selenium kustom kami sendiri, SiteTestUI alias STUI
  • Transisi dari STUI ke WebdriverIO berbasis Node
  • Tidak puas dengan kedua penyiapan dan akhirnya bermigrasi ke Cypress

Posting blog ini adalah salah satu dari dua bagian yang mendokumentasikan dan menyoroti pengalaman, pelajaran, dan pengorbanan kami dengan masing-masing pendekatan yang digunakan untuk memandu Anda dan pengembang lain tentang cara menghubungkan pengujian E2E dengan pola dan strategi pengujian yang bermanfaat.

Bagian pertama mencakup perjuangan awal kami dengan STUI, bagaimana kami bermigrasi ke WebdriverIO, namun masih mengalami banyak kegagalan serupa dengan STUI. Kami akan membahas cara kami menulis pengujian dengan WebdriverIO, melakukan Dockerisasi pengujian untuk dijalankan dalam wadah, dan akhirnya mengintegrasikan pengujian dengan Buildkite, penyedia CICD kami.

Jika Anda ingin melompat ke tempat kita berada dengan pengujian E2E hari ini, silakan lanjutkan ke bagian dua saat melewati migrasi terakhir kami dari STUI dan WebdriverIO ke Cypress dan bagaimana kami mengaturnya di berbagai tim.

TLDR: Kami mengalami kesulitan dan kesulitan yang sama dengan solusi pembungkus Selenium, STUI dan WebdriverIO, sehingga kami akhirnya mulai mencari alternatif di Cypress. Kami belajar banyak pelajaran mendalam untuk menangani penulisan tes E2E dan berintegrasi dengan Docker dan Buildkite.

Daftar isi:

Terjun Pertama ke Pengujian E2E: siteTESUI alias STUI

Beralih dari STUI ke WebdriverIO

Langkah 1: Memutuskan Ketergantungan untuk WebdriverIO

Langkah 2: Konfigurasi dan Skrip Lingkungan

Langkah 3: Menerapkan Tes ENE Secara Lokal

Langkah 4: Dockerizing semua Tes

Langkah 5: Mengintegrasikan dengan CICD

Pengorbanan dengan WebdriverIo

Pindah ke Cypress

Terjun Pertama ke Pengujian E2E: SiteTestUI alias STUI

Ketika awalnya mencari alat otomatisasi browser, SDET kami (insinyur pengembangan perangkat lunak dalam pengujian) terjun ke dalam membuat solusi internal kami sendiri yang dibuat dengan Ruby dan Selenium, khususnya Rspec dan kerangka kerja Selenium khusus yang disebut Gridium. Kami menghargai dukungan lintas-browsernya, kemampuan untuk mengonfigurasi integrasi kustom kami sendiri dengan TestRail untuk kasus pengujian insinyur QA (Jaminan Kualitas), dan pemikiran untuk membangun repo yang ideal bagi semua tim frontend untuk menulis pengujian E2E di satu lokasi dan menjadi berjalan sesuai jadwal.

Sebagai pengembang frontend yang ingin menulis beberapa pengujian E2E untuk pertama kalinya dengan alat yang dibuat oleh SDET untuk kami, kami mulai menerapkan pengujian untuk halaman yang telah kami rilis dan merenungkan cara menyiapkan pengguna dan data seed dengan benar untuk fokus pada bagian fitur yang ingin kami uji. Kami mempelajari beberapa hal hebat di sepanjang jalan seperti membentuk objek halaman untuk mengatur fungsionalitas pembantu dan penyeleksi elemen yang ingin kami interaksikan dengan halaman dan mulai membentuk spesifikasi yang mengikuti struktur ini:

Kami secara bertahap membangun rangkaian pengujian substansial di berbagai tim dalam repo yang sama mengikuti pola yang sama, tetapi kami segera mengalami banyak frustrasi yang akan sangat memperlambat kemajuan kami untuk pengembang baru dan kontributor yang konsisten untuk STUI seperti:

  • Memulai dan menjalankan membutuhkan waktu dan upaya yang cukup besar untuk menginstal semua driver browser, dependensi Ruby Gem, dan versi yang benar bahkan sebelum menjalankan suite pengujian. Kami terkadang harus mencari tahu mengapa pengujian akan berjalan di mesin seseorang versus mesin orang lain dan bagaimana pengaturannya berbeda.
  • Test suite berkembang biak dan berjalan selama berjam-jam sampai selesai. Karena semua tim berkontribusi pada repo yang sama, menjalankan semua pengujian secara serial berarti menunggu beberapa jam agar rangkaian pengujian keseluruhan berjalan dan beberapa tim yang mendorong kode baru berpotensi menyebabkan pengujian rusak lainnya di tempat lain.
  • Kami menjadi frustrasi dengan penyeleksi CSS yang tidak stabil dan penyeleksi XPath yang rumit . Gambar di bawah ini cukup menjelaskan bagaimana menggunakan XPath dapat membuat segalanya menjadi lebih rumit dan ini adalah beberapa yang lebih sederhana.

  • Tes debug itu menyakitkan . Kami mengalami kesulitan men-debug keluaran kesalahan yang tidak jelas dan kami biasanya tidak tahu di mana dan bagaimana hal itu gagal. Kami hanya bisa berulang kali menjalankan tes dan mengamati browser untuk menyimpulkan di mana kemungkinan gagal dan kode mana yang bertanggung jawab untuk itu. Ketika pengujian gagal di lingkungan Docker di CICD tanpa banyak melihat selain output konsol, kami berjuang untuk mereproduksi secara lokal dan untuk memecahkan masalah.
  • Kami menemukan bug Selenium dan kelambatan . Pengujian berjalan lambat karena semua permintaan dikirim dari server ke browser dan terkadang pengujian kami akan macet sama sekali saat mencoba memilih banyak elemen pada halaman atau karena alasan yang tidak diketahui selama pengujian berjalan.
  • Lebih banyak waktu dihabiskan untuk memperbaiki dan melewatkan pengujian dan pengujian build terjadwal yang rusak mulai diabaikan. Tes tidak memberikan nilai yang benar-benar menandakan kesalahan yang sebenarnya dalam sistem.
  • Tim frontend kami merasa terputus dari pengujian E2E karena ada dalam repo terpisah dari masing-masing aplikasi web. Kami sering kali harus membuka kedua repo secara bersamaan dan terus melihat bolak-balik antara basis kode selain tab browser saat pengujian berjalan.
  • Tim frontend tidak menyukai peralihan konteks dari menulis kode dalam JavaScript atau TypeScript setiap hari ke Ruby dan harus mempelajari kembali cara menulis tes setiap kali berkontribusi ke STUI.
  • Karena ini adalah pengambilan pertama bagi banyak dari kami ketika berkontribusi pada pengujian, kami jatuh ke dalam banyak antipattern seperti membangun status melalui UI untuk masuk, tidak melakukan pembongkaran atau penyiapan yang cukup melalui API, dan tidak memiliki dokumentasi yang cukup untuk mengikuti apa yang membuat ujian besar.

Terlepas dari kemajuan kami dalam menulis sejumlah besar tes E2E untuk banyak tim yang berbeda dalam satu repo, dan mempelajari beberapa pola bermanfaat untuk dibawa bersama kami, kami mengalami sakit kepala dengan pengalaman pengembang secara keseluruhan, banyak titik kegagalan, dan kurangnya tes yang berharga dan stabil untuk memverifikasi seluruh tumpukan kami.

Kami menghargai cara untuk memberdayakan pengembang frontend dan QA lain untuk membangun suite pengujian E2E stabil mereka sendiri dengan JavaScript yang berada dengan kode aplikasi mereka sendiri untuk mempromosikan penggunaan kembali, kedekatan, dan kepemilikan pengujian. Hal ini mendorong kami untuk menyelidiki WebdriverIO, kerangka kerja Selenium berbasis JavaScript untuk pengujian otomatisasi browser, sebagai pengganti awal kami untuk STUI, solusi internal Ruby Selenium kustom.

Kami kemudian akan mengalami kejatuhannya dan akhirnya pindah ke Cypress (maju cepat ke Bagian 2 di sini jika hal-hal WebdriverIO tidak menarik bagi Anda), tetapi kami memperoleh pengalaman yang tak ternilai dengan membangun infrastruktur standar di setiap repo tim, mengintegrasikan tes E2E ke CICD untuk frontend kami tim, dan mengadopsi pola teknis yang layak didokumentasikan dalam perjalanan kami dan untuk dipelajari orang lain tentang siapa yang mungkin akan terjun ke WebdriverIO atau solusi pengujian E2E lainnya.

Beralih dari STUI ke WebdriverIO

Saat memulai WebdriverIO dengan harapan mengurangi frustrasi yang kami alami, kami bereksperimen dengan meminta setiap tim frontend mengonversi pengujian otomatisasi yang ada yang ditulis dengan pendekatan Ruby Selenium ke pengujian WebdriverIO dalam JavaScript atau TypeScript dan membandingkan stabilitas, kecepatan, pengalaman pengembang, dan pemeliharaan keseluruhan tes.

Untuk mencapai pengaturan ideal kami untuk memiliki tes E2E yang berada di repo aplikasi tim frontend dan berjalan di CICD dan jalur pipa terjadwal, kami merangkum langkah-langkah berikut yang umumnya berlaku untuk tim mana pun yang ingin menggunakan kerangka kerja pengujian E2E dengan tujuan yang sama :

  1. Menginstal dan memilih dependensi untuk dihubungkan dengan kerangka pengujian
  2. Menetapkan konfigurasi lingkungan dan perintah skrip
  3. Menerapkan tes E2E yang lulus secara lokal terhadap lingkungan yang berbeda
  4. Dockerizing tes
  5. Mengintegrasikan pengujian Dockerized dengan penyedia CICD

Langkah 1: Memutuskan Ketergantungan untuk WebdriverIO

WebdriverIO memberi pengembang fleksibilitas untuk memilih di antara banyak kerangka kerja, reporter, dan layanan untuk memulai uji coba. Ini membutuhkan banyak mengutak-atik dan penelitian bagi tim untuk menetap di perpustakaan tertentu untuk memulai.

Karena WebdriverIO tidak menentukan tentang apa yang harus digunakan, ini membuka pintu bagi tim frontend untuk memiliki berbagai perpustakaan dan konfigurasi, meskipun tes inti keseluruhan akan konsisten dalam menggunakan WebdriverIO API.

Kami memilih untuk membiarkan masing-masing tim frontend menyesuaikan berdasarkan preferensi mereka dan kami biasanya menggunakan Mocha sebagai kerangka pengujian, Mochawesome sebagai reporter, layanan Selenium Standalone, dan dukungan TypeScript. Kami memilih Mocha dan Mochawesome karena keakraban tim kami dan pengalaman sebelumnya dengan Mocha sebelumnya, tetapi tim lain memutuskan untuk menggunakan alternatif lain juga.

Langkah 2: Konfigurasi dan Skrip Lingkungan

Setelah memutuskan infrastruktur WebdriverIO, kami membutuhkan cara agar pengujian WebdriverIO kami berjalan dengan berbagai pengaturan untuk setiap lingkungan. Berikut adalah daftar yang menggambarkan sebagian besar kasus penggunaan tentang bagaimana kami ingin menjalankan pengujian ini dan mengapa kami ingin mendukungnya:

  • Terhadap server dev Webpack yang berjalan di localhost (yaitu http://localhost:8000) dan server dev tersebut akan diarahkan ke API lingkungan tertentu seperti pengujian atau pementasan (yaitu https://testing.api.com atau https:// staging.api.com).
    Mengapa? Terkadang kami perlu membuat perubahan pada aplikasi web lokal kami seperti menambahkan pemilih yang lebih spesifik agar pengujian kami dapat berinteraksi dengan elemen dengan cara yang lebih kuat atau kami sedang mengembangkan fitur baru dan perlu menyesuaikan dan memvalidasi pengujian otomatisasi yang ada akan diteruskan secara lokal terhadap perubahan kode baru kami. Setiap kali kode aplikasi berubah dan kami belum mendorong ke lingkungan yang diterapkan, kami menggunakan perintah ini untuk menjalankan pengujian kami terhadap aplikasi web lokal kami.
  • Terhadap aplikasi yang diterapkan untuk lingkungan tertentu (yaitu https://testing.app.com atau https://staging.app.com) seperti pengujian atau pementasan
    Mengapa? Di lain waktu kode aplikasi tidak berubah tetapi kami mungkin harus mengubah kode pengujian kami untuk memperbaiki beberapa kelemahan atau kami merasa cukup percaya diri untuk menambah atau menghapus pengujian sama sekali tanpa membuat perubahan frontend. Kami banyak menggunakan perintah ini untuk memperbarui atau men-debug pengujian secara lokal terhadap aplikasi yang diterapkan untuk mensimulasikan lebih dekat bagaimana pengujian kami berjalan di pipeline CICD.
  • Berjalan dalam wadah Docker terhadap aplikasi yang diterapkan untuk lingkungan tertentu seperti pengujian atau pementasan
    Mengapa? Ini dimaksudkan untuk pipeline CICD sehingga kami dapat memicu pengujian E2E untuk dijalankan dalam container Docker, misalnya untuk staging aplikasi yang di-deploy dan memastikannya lolos sebelum men-deploy kode ke produksi atau dalam pengujian terjadwal dalam pipeline khusus. Saat mengatur perintah ini pada awalnya, kami melakukan banyak percobaan dan kesalahan untuk memutar wadah Docker dengan nilai variabel lingkungan yang berbeda dan menguji untuk melihat pengujian yang tepat berhasil dijalankan sebelum menghubungkannya dengan penyedia CICD kami, Buildkite.

Untuk mencapai ini, kami menyiapkan file konfigurasi dasar umum dengan properti bersama dan banyak file khusus lingkungan sehingga setiap file konfigurasi lingkungan akan bergabung dengan file dasar dan menimpa atau menambahkan properti seperti yang diminta untuk dijalankan. Kami dapat memiliki satu file untuk setiap lingkungan tanpa memerlukan file dasar, tetapi itu akan menyebabkan banyak duplikasi dalam pengaturan umum. Kami memilih untuk menggunakan perpustakaan seperti deepmerge untuk menanganinya bagi kami, tetapi penting untuk dicatat bahwa penggabungan tidak selalu sempurna dengan objek atau larik bersarang. Selalu periksa kembali konfigurasi keluaran yang dihasilkan karena dapat menyebabkan perilaku tidak terdefinisi ketika ada properti duplikat yang tidak digabungkan dengan benar.

Kami membentuk file konfigurasi dasar umum , wdio.conf.js , seperti ini:

Agar sesuai dengan kasus penggunaan utama pertama kami dalam menjalankan tes E2E terhadap server dev webpack lokal yang menunjuk ke API lingkungan, kami membuat file konfigurasi localhost, wdio.localhost.conf.js , dengan yang berikut:

Perhatikan kami menggabungkan file dasar dan menambahkan properti khusus localhost ke file agar lebih ringkas dan lebih mudah dirawat. Kami juga menggunakan layanan Selenium Standalone untuk menjalankan berbagai jenis browser alias kemampuan.

Untuk kasus penggunaan kedua dalam menjalankan pengujian E2E terhadap aplikasi web yang diterapkan, kami menyiapkan file konfigurasi aplikasi pengujian dan staging , `wdio.testing.conf.js` dan wdio.staging.conf.js , mirip dengan ini:

Di sini kami menambahkan beberapa variabel lingkungan tambahan ke file konfigurasi seperti kredensial masuk ke pengguna khusus pada staging dan memperbarui `baseUrl` untuk menunjuk ke URL aplikasi staging yang diterapkan.

Untuk kasus penggunaan ketiga dalam menjalankan pengujian E2E dalam wadah Docker terhadap aplikasi web yang diterapkan dalam ranah penyedia CICD kami, kami menyiapkan file konfigurasi CICD , wdio.cicd.testing.conf.js dan wdio.cicd.staging.conf.js , seperti:

Perhatikan bagaimana kami tidak lagi menggunakan layanan Selenium Standalone karena nanti kami akan menginstal Selenium Chrome, Selenium Hub, dan kode aplikasi di layanan terpisah dalam file Docker Compose. Konfigurasi ini juga menunjukkan variabel lingkungan yang sama dengan konfigurasi staging seperti kredensial login dan `baseUrl` karena kami berharap untuk menjalankan pengujian terhadap aplikasi staging yang diterapkan, dan satu-satunya perbedaan adalah pengujian ini dimaksudkan untuk dijalankan dalam container Docker .

Dengan file konfigurasi lingkungan ini dibuat, kami menguraikan perintah skrip package.json yang akan berfungsi sebagai dasar untuk pengujian kami. Untuk contoh ini, kami mengawali perintah dengan "utest" untuk menunjukkan pengujian UI dengan WebdriverIO dan karena kami juga mengakhiri file pengujian dengan *.uitest.js . Berikut adalah beberapa contoh perintah untuk lingkungan staging:

Langkah 3: Menerapkan Tes E2E Secara Lokal

Dengan semua perintah pengujian yang ada, kami melakukan pengujian di repo STUI kami untuk dikonversi ke pengujian WebdriverIO. Kami berfokus pada pengujian halaman berukuran kecil hingga sedang dan mulai menerapkan pola objek halaman untuk merangkum semua UI untuk setiap halaman secara terorganisir.

Kami dapat memiliki file terstruktur dengan sekelompok fungsi pembantu atau literal objek atau strategi lainnya, tetapi kuncinya adalah memiliki cara yang konsisten untuk memberikan pengujian yang dapat dipelihara dengan cepat dan tetap menggunakannya. Jika aliran UI atau elemen DOM berubah untuk halaman tertentu, kami hanya perlu memfaktorkan ulang objek halaman yang terkait dengannya dan mungkin kode pengujian agar pengujian lulus lagi.

Kami menerapkan pola objek halaman dengan memiliki objek halaman dasar dengan fungsionalitas bersama yang darinya semua objek halaman lainnya akan diperluas. Kami memiliki fungsi seperti open untuk menyediakan API yang konsisten di semua objek halaman kami untuk "membuka" atau mengunjungi URL halaman di browser. Itu menyerupai sesuatu seperti ini:

Menerapkan objek halaman tertentu mengikuti pola perluasan yang sama dari kelas Page dasar dan menambahkan pemilih ke elemen tertentu yang ingin kita interaksikan atau tegaskan dan fungsi pembantu untuk melakukan tindakan pada halaman.

Perhatikan bagaimana kita menggunakan kelas dasar open through super.open(...) dengan rute khusus halaman sehingga kita dapat mengunjungi halaman dengan panggilan ini, SomePage.open() . Kami juga mengekspor kelas yang sudah diinisialisasi sehingga kami dapat mereferensikan elemen seperti SomePage.submitButton atau SomePage.tableRows dan berinteraksi dengan atau menegaskan elemen tersebut dengan perintah WebdriverIO. Jika objek halaman dimaksudkan untuk dibagikan dan diinisialisasi dengan properti anggotanya sendiri dalam konstruktor, kita juga dapat mengekspor kelas secara langsung dan membuat instance objek halaman dalam file pengujian dengan new SomePage(...constructorArgs) .

Setelah kami meletakkan objek halaman dengan selektor dan beberapa fungsi pembantu, kami kemudian menulis tes E2E dan biasanya memodelkan rumus pengujian ini:

  • Siapkan atau hancurkan melalui API apa yang diperlukan untuk menyetel ulang kondisi pengujian ke titik awal yang diharapkan sebelum menjalankan pengujian yang sebenarnya.
  • Masuk ke pengguna khusus untuk pengujian sehingga setiap kali kami mengunjungi halaman secara langsung, kami akan tetap masuk dan tidak harus melalui UI. Kami membuat fungsi pembantu login sederhana yang mengambil nama pengguna dan kata sandi yang membuat panggilan API yang sama dengan yang kami gunakan untuk halaman login kami dan yang pada akhirnya mengembalikan token autentikasi kami yang diperlukan untuk tetap masuk dan meneruskan header permintaan API yang dilindungi. Perusahaan lain mungkin memiliki lebih banyak titik akhir atau alat internal khusus untuk membuat pengguna baru dengan data awal dan konfigurasi dengan cepat, tetapi sayangnya, kami tidak memilikinya yang cukup lengkap. Kami akan melakukannya dengan cara kuno dan membuat pengguna pengujian khusus di lingkungan kami dengan konfigurasi yang berbeda melalui UI dan sering kali memecah pengujian untuk halaman dengan pengguna yang berbeda untuk menghindari bentrokan sumber daya dan tetap terisolasi saat pengujian berjalan secara paralel. Kami harus memastikan pengguna tes khusus tidak disentuh oleh orang lain atau tes akan rusak ketika seseorang tanpa sadar mengutak-atik salah satu dari mereka.
  • Otomatiskan langkah-langkah tersebut seolah-olah pengguna akhir akan berinteraksi dengan fitur/halaman. Biasanya, kami akan mengunjungi halaman yang menampung aliran fitur kami dan mulai mengikuti langkah-langkah tingkat tinggi yang akan dilakukan pengguna akhir seperti mengisi input, mengklik tombol, menunggu modals atau spanduk muncul, dan mengamati tabel untuk output yang diubah sebagai akibat dari tindakan tersebut. Dengan menggunakan objek dan penyeleksi halaman praktis kami, kami dengan cepat menerapkan setiap langkah dan, sebagai pemeriksaan kewarasan di sepanjang jalan, kami akan menegaskan tentang apa yang harus atau tidak boleh dilihat pengguna di halaman selama aliran fitur untuk memastikan hal-hal tertentu berperilaku seperti yang diharapkan sebelum dan sesudah setiap langkah. Kami juga sengaja memilih pengujian jalur bahagia bernilai tinggi dan terkadang status kesalahan umum yang dapat direproduksi dengan mudah, menunda pengujian tingkat yang lebih rendah ke pengujian unit dan integrasi.

Berikut adalah contoh kasar dari tata letak umum pengujian E2E kami (strategi ini juga diterapkan pada kerangka pengujian lain yang kami coba):

Di samping catatan, kami memilih untuk tidak membahas semua tip dan gotchas untuk praktik terbaik WebdriverIO dan E2E dalam seri posting blog ini, tetapi kami akan membicarakan topik tersebut di posting blog mendatang, jadi tetap ikuti perkembangannya!

Langkah 4: Dockerizing semua Tes

Saat menjalankan setiap langkah pipeline Buildkite pada mesin AWS baru di cloud, kami tidak bisa begitu saja memanggil “npm run utests:staging” karena mesin tersebut tidak memiliki Node, browser, kode aplikasi kami, atau dependensi lainnya untuk benar-benar menjalankan pengujian .

Untuk mengatasi ini, kami menggabungkan semua dependensi seperti Node, Selenium, Chrome, dan kode aplikasi dalam wadah Docker agar pengujian WebdriverIO berjalan dengan sukses. Kami memanfaatkan Docker dan Docker Compose untuk merakit semua layanan yang diperlukan untuk memulai dan menjalankannya, yang diterjemahkan ke dalam Dockerfiles dan docker-compose.yml compose.yml dan banyak eksperimen dengan memutar container Docker secara lokal agar semuanya berfungsi.

Untuk memberikan lebih banyak konteks, kami bukan ahli di Docker, jadi perlu waktu yang cukup lama untuk memahami cara menyatukan semuanya. Ada banyak cara untuk menguji Dockerize WebdriverIO dan kami merasa sulit untuk mengatur banyak layanan yang berbeda bersama-sama dan menyaring berbagai gambar Docker, versi Compose, dan tutorial hingga semuanya berhasil.

Kami akan mendemonstrasikan sebagian besar file yang disempurnakan yang cocok dengan salah satu konfigurasi tim kami dan kami berharap ini memberikan wawasan bagi Anda atau siapa pun yang menangani masalah umum pengujian berbasis Dockerizing Selenium.

Pada tingkat tinggi, pengujian kami menuntut hal berikut:

  • Selenium untuk menjalankan perintah melawan dan berkomunikasi dengan browser. Kami menggunakan Selenium Hub untuk memutar beberapa instance sesuka hati dan mengunduh gambar, "selenium/hub", untuk layanan selenium-hub di file docker-compose.
  • Sebuah browser untuk melawan. Kami memunculkan contoh Selenium Chrome dan memasang gambar, “selenium/node-chrome-debug”, untuk layanan selenium-chrome di file docker-compose.yml file .
  • Kode aplikasi untuk menjalankan file pengujian kami dengan modul Node lain yang diinstal. Kami membuat Dockerfile baru untuk menyediakan lingkungan dengan Node untuk menginstal paket npm dan menjalankan skrip package.json , menyalin kode pengujian, dan menetapkan layanan yang didedikasikan untuk menjalankan file pengujian bernama uitests di file docker-compose.yml .

Untuk memunculkan layanan dengan semua aplikasi kami dan kode pengujian yang diperlukan untuk menjalankan tes WebdriverIO, kami membuat Dockerfile bernama Dockerfile.uitests dan menginstal semua node_modules dan menyalin kode ke direktori kerja gambar di lingkungan Node. Ini akan digunakan oleh layanan Docker Compose kami dan kami mencapai Dockerfile uitests cara berikut:

Untuk memunculkan Selenium Hub, browser Chrome, dan kode pengujian aplikasi bersama-sama untuk menjalankan pengujian WebdriverIO, kami menguraikan layanan selenium-hub , selenium-chrom e, dan uitest dalam file docker-compose.uitests.yml :

Kami menghubungkan gambar Selenium Hub dan Chrome melalui variabel lingkungan, depends_on , dan mengekspos port ke layanan. Gambar kode aplikasi pengujian kami pada akhirnya akan didorong dan ditarik dari registri Docker pribadi yang kami kelola.

Kami akan membuat gambar Docker untuk kode pengujian selama CICD dengan variabel lingkungan tertentu seperti VERSION dan PIPELINE_SUFFIX untuk mereferensikan gambar dengan tag dan nama yang lebih spesifik. Kami kemudian akan memulai layanan Selenium dan menjalankan perintah melalui layanan uitests untuk menjalankan tes WebdriverIO.

Saat kami membangun file Docker Compose kami, kami memanfaatkan perintah bermanfaat seperti docker-compose up dan docker-compose down dengan Mac Docker yang diinstal pada mesin kami untuk menguji secara lokal gambar kami memiliki konfigurasi yang tepat dan berjalan dengan lancar sebelum berintegrasi dengan Buildkite. Kami mendokumentasikan semua perintah yang diperlukan untuk membuat gambar yang diberi tag, mendorongnya ke registri, menariknya ke bawah, dan menjalankan pengujian sesuai dengan nilai variabel lingkungan.

Langkah 5: Mengintegrasikan dengan CICD

Setelah kami menetapkan perintah Docker yang berfungsi dan pengujian kami berjalan dengan sukses dalam wadah Docker terhadap lingkungan yang berbeda, kami mulai berintegrasi dengan Buildkite, penyedia CICD kami.

Buildkite menyediakan cara untuk mengeksekusi langkah-langkah dalam file .yml pada mesin AWS kami dengan skrip Bash dan variabel lingkungan yang disetel baik melalui kode atau UI pengaturan Buildkite untuk pipeline repo kami.

Buildkite juga memungkinkan kami untuk memicu pipeline pengujian ini dari pipeline penerapan utama kami dengan variabel lingkungan yang diekspor dan kami akan menggunakan kembali langkah pengujian ini untuk pipeline pengujian terisolasi lainnya yang akan berjalan sesuai jadwal untuk dipantau dan dilihat oleh QA kami.

Pada tingkat tinggi, pipeline Buildkite pengujian kami untuk WebdriverIO dan yang lebih baru Cypress berbagi langkah serupa berikut:

  • Siapkan gambar Docker . Bangun, beri tag, dan dorong gambar Docker yang diperlukan untuk pengujian ke registri sehingga kami dapat menariknya ke bawah di langkah selanjutnya.
  • Jalankan tes berdasarkan konfigurasi variabel lingkungan . Tarik gambar Docker yang diberi tag untuk build tertentu dan jalankan perintah yang tepat terhadap lingkungan yang diterapkan untuk menjalankan rangkaian pengujian yang dipilih dari variabel lingkungan yang ditetapkan.

Berikut adalah contoh dekat dari file pipeline.uitests.yml yang mendemonstrasikan penyiapan gambar Docker di langkah "Build UITests Docker Image" dan menjalankan pengujian di langkah "Jalankan pengujian Webdriver terhadap Chrome":

Satu hal yang perlu diperhatikan adalah langkah pertama, “Build UITests Docker Image”, dan cara menyiapkan image Docker untuk pengujian. Itu menggunakan perintah build Docker Compose untuk membangun layanan uitests dengan semua kode pengujian aplikasi dan menandainya dengan variabel lingkungan latest dan ${VERSION} sehingga kami akhirnya dapat menarik gambar yang sama dengan tag yang tepat untuk build ini di masa mendatang melangkah.

Setiap langkah dapat dijalankan pada mesin yang berbeda di cloud AWS di suatu tempat, sehingga tag secara unik mengidentifikasi gambar untuk menjalankan Buildkite tertentu. Setelah menandai gambar, kami mendorong gambar terbaru dan versi yang diberi tag ke registri Docker pribadi kami untuk digunakan kembali.

Pada langkah “Jalankan pengujian Webdriver terhadap Chrome”, kami menarik gambar yang kami buat, beri tag, dan dorong pada langkah pertama dan memulai Selenium Hub, Chrome, dan layanan pengujian. Berdasarkan variabel lingkungan seperti $UITESTENV dan $UITESTSUITE , kami akan memilih dan memilih jenis perintah untuk dijalankan seperti npm run uitest: dan suite pengujian untuk dijalankan untuk build Buildkite khusus ini seperti --suite $UITESTSUITE .

Variabel lingkungan ini akan ditetapkan melalui pengaturan pipa Buildkite atau akan dipicu secara dinamis dari skrip Bash yang akan mengurai bidang pilih Buildkite untuk menentukan rangkaian pengujian mana yang dijalankan dan terhadap lingkungan mana.

Berikut adalah contoh pengujian WebdriverIO yang dipicu dalam pipeline pengujian khusus, yang juga menggunakan kembali file pipeline.uitests.yml yang sama tetapi dengan variabel lingkungan yang ditetapkan tempat pipeline dipicu. Build ini gagal dan memiliki tangkapan layar kesalahan untuk kami lihat di bawah tab Artifacts dan keluaran konsol di bawah tab Logs . Ingat artifact_paths di pipeline.uitests.yml (https://Gist.github.com/alfredlucero/71032a82f3a72cb2128361c08edbcff2#file-pipeline-utests-yml-L38), setelan tangkapan layar untuk `mochawesome` di `wdio.conf.js ` (https://Gist.github.com/alfredlucero/4ee280be0e0674048974520b79dc993a#file-wdio-conf-js-L39), dan pemasangan volume di layanan `utests` di `docker-compose.utests.yml` (https://Gist.github.com/alfredlucero/d2df4533a4a49d5b2f2c4a0eb5590ff8#file-docker-compose-yml-L32)?

Kami dapat menghubungkan tangkapan layar agar dapat diakses melalui UI Buildkite untuk kami unduh secara langsung dan lihat langsung untuk membantu pengujian debug seperti yang ditunjukkan di bawah ini.

Contoh lain dari pengujian WebdriverIO yang berjalan di pipeline terpisah pada jadwal untuk halaman tertentu menggunakan file pipeline.uitests.yml kecuali dengan variabel lingkungan yang sudah dikonfigurasi dalam pengaturan pipeline Buildkite ditampilkan di bawahnya.

Penting untuk dicatat bahwa setiap penyedia CICD memiliki fungsionalitas dan cara yang berbeda untuk mengintegrasikan langkah-langkah ke dalam semacam proses penerapan saat menggabungkan kode baru apakah itu melalui file .yml dengan sintaks tertentu, pengaturan GUI, skrip Bash, atau cara lain apa pun.

Saat kami beralih dari Jenkins ke Buildkite, kami secara drastis meningkatkan kemampuan tim untuk menentukan pipeline mereka sendiri dalam basis kode masing-masing, memparalelkan langkah-langkah di seluruh mesin penskalaan sesuai permintaan, dan memanfaatkan perintah yang lebih mudah dibaca.

Terlepas dari penyedia CICD yang mungkin Anda gunakan, strategi pengintegrasian pengujian akan serupa dalam menyiapkan image Docker dan menjalankan pengujian berdasarkan variabel lingkungan untuk portabilitas dan fleksibilitas.

Pengorbanan dengan WebdriverIO

Setelah mengonversi sejumlah besar pengujian solusi Ruby Selenium kustom ke pengujian WebdriverIO dan berintegrasi dengan Docker dan Buildkite, kami meningkatkan di beberapa area tetapi masih merasakan kesulitan yang serupa dengan sistem lama yang pada akhirnya membawa kami ke perhentian berikutnya dan terakhir dengan Cypress untuk solusi pengujian E2E kami.

Berikut adalah daftar beberapa pro yang kami temukan dari pengalaman kami dengan WebdriverIO dibandingkan dengan solusi Ruby Selenium kustom:

  • Tes ditulis murni dalam JavaScript atau TypeScript daripada Ruby . Ini berarti lebih sedikit peralihan konteks antar bahasa dan lebih sedikit waktu yang dihabiskan untuk mempelajari kembali Ruby setiap kali kami menulis tes E2E.
  • Kami menempatkan pengujian dengan kode aplikasi alih-alih menggunakan repo bersama Ruby. Kami tidak lagi merasa bergantung pada kegagalan tes tim lain dan mengambil lebih banyak kepemilikan langsung dari tes E2E untuk fitur kami di repo kami.
  • Kami menghargai opsi pengujian lintas-browser . Dengan WebdriverIO kami dapat melakukan pengujian terhadap berbagai kemampuan atau browser seperti Chrome, Firefox, dan IE, meskipun kami berfokus terutama pada menjalankan pengujian kami terhadap Chrome karena lebih dari 80% pengguna kami mengunjungi aplikasi kami melalui Chrome.
  • Kami menghibur kemungkinan berintegrasi dengan layanan pihak ketiga . Dokumentasi WebdriverIO menjelaskan cara berintegrasi dengan layanan pihak ketiga seperti BrowserStack dan SauceLabs untuk membantu mencakup aplikasi kami di semua perangkat dan browser.
  • Kami memiliki fleksibilitas untuk memilih runner, reporter, dan layanan pengujian kami sendiri . WebdriverIO tidak menentukan apa yang harus digunakan sehingga setiap tim mengambil kebebasan dalam memutuskan apakah akan menggunakan hal-hal seperti Mocha dan Chai atau Jest dan layanan lainnya atau tidak. Ini juga dapat diartikan sebagai penipu karena tim mulai menyimpang dari pengaturan masing-masing dan itu membutuhkan banyak waktu untuk bereksperimen dengan masing-masing opsi untuk kami pilih.
  • WebdriverIO API, CLI, dan dokumentasi cukup berguna untuk menulis pengujian dan berintegrasi dengan Docker dan CIC D. Kami dapat memiliki banyak file konfigurasi yang berbeda, mengelompokkan spesifikasi, menjalankan pengujian melalui baris perintah, dan menulis pengujian mengikuti pola objek halaman. Namun, dokumentasinya bisa lebih jelas dan kami harus menggali banyak bug aneh. Meskipun demikian, kami dapat mengonversi pengujian kami dari solusi Ruby Selenium.

Kami membuat kemajuan di banyak area yang tidak kami miliki dalam solusi Ruby Selenium sebelumnya, tetapi kami menemukan banyak showstoppers yang mencegah kami masuk semua dengan WebdriverIO seperti berikut:

  • Karena WebdriverIO masih berbasis Selenium , kami mengalami banyak waktu tunggu yang aneh, crash, dan bug, mengingatkan kami akan kilas balik negatif dengan solusi Ruby Selenium lama kami. Terkadang pengujian kami akan macet sama sekali saat kami memilih banyak elemen pada halaman dan pengujian akan berjalan lebih lambat dari yang kami inginkan. Kami harus mencari solusi melalui banyak masalah Github atau menghindari metodologi tertentu saat menulis tes.
  • Pengalaman pengembang secara keseluruhan kurang optimal . Dokumentasi memberikan beberapa gambaran umum tingkat tinggi dari perintah tetapi tidak cukup contoh untuk menjelaskan semua cara menggunakannya. Kami menghindari penulisan tes E2E dengan Ruby dan akhirnya harus menulis tes dalam JavaScript atau TypeScript, tetapi WebdriverIO API agak membingungkan untuk ditangani. Beberapa contoh umum adalah penggunaan $ vs. $$ untuk elemen tunggal vs. jamak, $('...').waitForVisible(9000, true) untuk menunggu elemen tidak terlihat, dan perintah tidak intuitif lainnya. Kami mengalami banyak penyeleksi yang tidak stabil dan harus secara eksplisit $(...).waitForVisible() untuk semuanya.
  • Tes debug sangat menyakitkan dan membosankan bagi pengembang dan QA. Whenever tests failed, we only had screenshots, which would often be blank or not capturing the right moment for us to deduce what went wrong, and vague console error messages that did not point us in the right direction of how to solve the problem and even where the issue occurred. We often had to re-run the tests many times and stare closely at the Chrome browser running the tests to hopefully put things together as to where in the code our tests failed. We used things like browser.debug() but it often did not work or did not provide enough information. We gradually gathered a bunch of console error messages and mapped them to possible solutions over time but it took lots of pain and headache to get there.
  • WebdriverIO tests were tough to set up with Docker . We struggled with trying to incorporate it into Docker as there were many tutorials and ways to do things in articles online, but it was hard to figure out a way that worked in general. Hooking up 2 to 3 services together with all these configurations led to long trial and error experiments and the documentation did not guide us enough in how to do that.
  • Choosing the test runner, reporter, assertions, and services demanded lots of research time upfront . Since WebdriverIO was flexible enough to allow other options, many teams had to spend plenty of time to even have a solid WebdriverIO infrastructure after experimenting with a lot of different choices and each team can have a completely different setup that doesn't transfer over well for shared knowledge and reuse.

To summarize our WebdriverIO and STUI comparison, we analyzed the overall developer experience (related to tools, writing tests, debugging, API, documentation, etc.), test run times, test passing rates, and maintenance as displayed in this table:

Moving On to Cypress

At the end of the day, our WebdriverIO tests were still flaky and tough to maintain. More time was still spent debugging tests in dealing with weird Selenium issues, vague console errors, and somewhat useful screenshots than actually reaping the benefits of seeing tests fail for when the backend or frontend encountered issues.

We appreciated cross-browser testing and implementing tests in JavaScript, but if our tests could not pass consistently without much headache for even Chrome, then it became no longer worth it and we would then simply have a STUI 2.0.

With WebdriverIO we still strayed from the crucial aspect of providing a way to write consistent, debuggable, maintainable, and valuable E2E automation tests for our frontend applications in our original goal. Overall, we learned a lot about integrating with Buildkite and Docker, using page objects, and outlining tests in a structured way that will transfer over to our final solution with Cypress.

If we felt it was necessary to run our tests in multiple browsers and against various third-party services, we could always circle back to having some tests written with WebdriverIO, or if we needed something fully custom, we would revisit the STUI solution.

Ultimately, neither solution met our main goal for E2E tests, so follow us on our journey in how we migrated from STUI and WebdriverIO to Cypress in part 2 of the blog post series.