Ide untuk Mengonfigurasi, Mengatur, dan Mengonsolidasikan Pengujian Cypress Anda #frontend@twiliosendgrid

Diterbitkan: 2020-12-15

Kami telah menulis banyak pengujian Cypress di berbagai aplikasi web dan tim frontend di Twilio SendGrid. Saat pengujian kami diskalakan ke banyak fitur dan halaman, kami menemukan beberapa opsi konfigurasi yang berguna dan mengembangkan cara untuk lebih mempertahankan jumlah file yang terus bertambah, penyeleksi elemen halaman kami, dan nilai batas waktu di sepanjang jalan.

Kami bertujuan untuk menunjukkan kepada Anda tip dan ide ini untuk mengonfigurasi, mengatur, dan mengkonsolidasikan hal-hal terkait Cypress Anda sendiri, jadi silakan lakukan dengan mereka apa yang terbaik untuk Anda dan tim Anda.

Posting blog ini mengasumsikan Anda memiliki pengetahuan tentang tes Cypress dan sedang mencari ide untuk memelihara dan meningkatkan tes Cypress Anda dengan lebih baik. Namun, jika Anda penasaran untuk mempelajari lebih lanjut tentang fungsi umum, pernyataan, dan pola yang mungkin berguna untuk menulis tes Cypress terhadap lingkungan yang terpisah, Anda dapat melihat posting blog ikhtisar seribu kaki ini.

Jika tidak, mari kita lanjutkan dan pertama-tama memandu Anda melalui beberapa tip konfigurasi untuk Cypress.

Mengonfigurasi cypress.json Anda

File cypress.json adalah tempat Anda dapat mengatur semua konfigurasi untuk pengujian Cypress Anda seperti batas waktu dasar untuk perintah Cypress, variabel lingkungan, dan properti lainnya.

Berikut adalah beberapa tip untuk Anda coba dalam konfigurasi Anda:

  • Sempurnakan batas waktu perintah dasar Anda, sehingga Anda tidak harus selalu menambahkan { timeout: timeoutInMs } di seluruh perintah Cypress Anda. Utak-atik angka sampai Anda menemukan keseimbangan yang tepat untuk pengaturan seperti "defaultCommandTimeout," "requestTimeout," dan "responseTimeout."
  • Jika pengujian Anda melibatkan iframe, kemungkinan besar Anda perlu menyetel "chromeWebSecurity" ke false sehingga Anda dapat mengakses iframe lintas-asal di aplikasi Anda.
  • Coba blokir skrip pemasaran, analitik, dan logging pihak ketiga saat Anda menjalankan tes Cypress untuk meningkatkan kecepatan dan tidak memicu peristiwa yang tidak diinginkan. Anda dapat dengan mudah mengatur daftar tolak dengan properti "blacklistHosts" mereka (atau properti "blockHosts" di Cypress 5.0.0), dengan mengambil larik gumpalan string yang cocok dengan jalur host pihak ketiga.
  • Sesuaikan dimensi viewport default dengan "viewportWidth" dan "viewportHeight" ketika Anda membuka Cypress GUI untuk membuat segalanya lebih mudah dilihat.
  • Jika ada masalah memori di Docker untuk pengujian yang berat atau Anda ingin membantu membuat segalanya lebih efisien, Anda dapat mencoba memodifikasi "numTestsKeptInMemory" ke angka yang lebih kecil dari default dan mengatur "videoUploadOnPasses" ke false untuk fokus mengunggah video untuk pengujian yang gagal berjalan saja.

Pada catatan lain, setelah mengubah konfigurasi Cypress Anda, Anda juga dapat menambahkan TypeScript dan mengetik lebih baik tes Cypress Anda seperti yang kami lakukan di posting blog ini. Ini sangat bermanfaat untuk pelengkapan otomatis, peringatan jenis, atau kesalahan saat memanggil dan merangkai fungsi (seperti cy.task('someTaskPluginFunction) , Cypress.env('someEnvVariable') , atau cy.customCommand() ).

Anda mungkin juga ingin bereksperimen dengan menyiapkan file cypress.json dasar dan memuat file konfigurasi Cypress terpisah untuk setiap lingkungan pengujian, seperti staging.json saat Anda menjalankan skrip Cypress yang berbeda di package.json Anda. Dokumentasi Cypress melakukan pekerjaan yang baik untuk memandu Anda melalui pendekatan ini.

Mengatur folder Cypress Anda

Cypress sudah menyiapkan folder cypress tingkat atas dengan struktur terpandu saat Anda pertama kali memulai Cypress di basis kode Anda. Ini termasuk folder lain seperti integration untuk file spesifikasi Anda, fixtures untuk file data JSON, plugins untuk cy.task(...) Anda dan konfigurasi lainnya, dan folder support untuk perintah dan jenis kustom Anda.

Aturan praktis yang baik untuk diikuti ketika mengatur dalam folder Cypress Anda, komponen React, atau kode apa pun secara umum adalah untuk menyatukan hal-hal yang cenderung berubah bersama. Dalam hal ini, karena kita berurusan dengan aplikasi web di browser, salah satu pendekatan yang dapat diskalakan dengan baik adalah dengan mengelompokkan berbagai hal dalam folder demi halaman atau fitur menyeluruh.

Kami membuat folder pages terpisah yang dipartisi berdasarkan nama fitur seperti "SenderAuthentication" untuk menampung semua objek halaman di bawah rute /settings/sender_auth seperti "DomainAuthentication" (/settings/sender_auth/domains/**/*) dan "LinkBranding" (/settings/sender_auth/links/**/*). Di folder plugins kami, kami juga melakukan hal yang sama dengan mengatur semua file plugin cy.task(...) untuk fitur atau halaman tertentu dalam folder yang sama untuk Otentikasi Pengirim, dan kami mengikuti pendekatan yang sama untuk folder integration kami untuk file spesifikasi. Kami telah menskalakan pengujian kami ke ratusan file spesifikasi, objek halaman, plugin, dan file lain di salah satu basis kode kami—dan itu mudah dan nyaman untuk dinavigasi.

Berikut adalah garis besar bagaimana kami mengatur folder cypress .

Satu hal lain yang perlu dipertimbangkan saat mengatur folder integration Anda (tempat semua spesifikasi Anda berada) berpotensi membagi file spesifikasi berdasarkan prioritas pengujian. Misalnya, Anda mungkin memiliki semua tes prioritas dan nilai tertinggi di folder "P1" dan tes prioritas kedua di folder "P2" sehingga Anda dapat dengan mudah menjalankan semua tes "P1" dengan menyetel opsi --spec seperti --spec 'cypress/integration/P1/**/*' .

Buat hierarki folder spesifikasi yang sesuai untuk Anda sehingga Anda dapat dengan mudah mengelompokkan spesifikasi tidak hanya berdasarkan halaman atau fitur seperti --spec 'cypress/integration/SomePage/**/*' , tetapi juga menurut beberapa kriteria lain seperti prioritas, produk , atau lingkungan.

Konsolidasi pemilih elemen

Saat mengembangkan komponen React halaman kami, kami biasanya menambahkan beberapa level unit dan tes integrasi dengan Jest dan Enzyme. Menjelang akhir pengembangan fitur, kami menambahkan lapisan pengujian Cypress E2E lainnya untuk memastikan semuanya berfungsi dengan backend. Baik pengujian unit untuk komponen React kami dan objek halaman untuk pengujian Cypress E2E kami memerlukan penyeleksi ke komponen/elemen DOM pada halaman yang ingin kami interaksikan dan tegaskan.

Saat kami memperbarui halaman dan komponen tersebut, ada kemungkinan ada penyimpangan dan kesalahan karena harus menyinkronkan beberapa tempat dari penyeleksi pengujian unit ke objek halaman Cypress ke kode komponen sebenarnya itu sendiri. Jika kita hanya mengandalkan nama kelas yang terkait dengan gaya, akan sulit untuk mengingat untuk memperbarui semua tempat yang mungkin rusak. Sebagai gantinya, kami menambahkan "data-hook", "data-testid", atau atribut "data-*" lainnya yang secara konsisten dinamai ke komponen dan elemen tertentu pada halaman yang ingin kami ingat dan tulis pemilih untuknya di lapisan pengujian kami.

Kami dapat menambahkan atribut "data-hook" ke banyak elemen kami, tetapi kami masih membutuhkan cara untuk mengelompokkannya bersama-sama di satu tempat untuk diperbarui dan digunakan kembali di file lain. Kami menemukan cara yang diketik untuk mengelola semua pemilih "kait data" ini untuk disebarkan ke komponen Bereaksi kami dan untuk digunakan dalam pengujian unit dan penyeleksi objek halaman kami untuk penggunaan kembali yang lebih banyak dan perawatan yang lebih mudah pada objek yang diekspor.

Untuk folder tingkat atas setiap halaman, kita akan membuat file hooks.ts yang mengelola dan mengekspor objek dengan nama kunci yang dapat dibaca untuk elemen dan pemilih string CSS aktual untuk elemen sebagai nilainya. Kami akan menyebutnya "Read Selectors" karena kami perlu membaca dan menggunakan formulir pemilih CSS untuk elemen dalam panggilan seperti Enzyme's wrapper.find(“[data-hook='selector']”) di unit test kami atau Cypress's cy.get(“[data-hook='selector']”) di objek halaman kita. Panggilan ini kemudian akan terlihat lebih bersih seperti wrapper.find(Selectors.someElement) atau cy.get(Selectors.someElement) .

Cuplikan kode berikut mencakup lebih banyak alasan dan cara kami menggunakan pemilih baca ini dalam praktik.

Demikian pula, kami juga mengekspor objek dengan nama kunci yang dapat dibaca untuk elemen dan dengan objek seperti { “data-hook”: “selector” } sebagai nilainya. Kita akan menyebutnya sebagai “Write Selectors” karena kita perlu menulis atau menyebarkan objek-objek ini ke dalam komponen React sebagai alat peraga untuk berhasil menambahkan atribut “data-hook” ke elemen yang mendasarinya. Tujuannya adalah untuk melakukan sesuatu seperti ini dan elemen DOM yang sebenarnya di bawahnya—dengan asumsi props diturunkan ke elemen JSX dengan benar—juga akan memiliki set atribut “data-hook=".

Cuplikan kode berikut mencakup lebih banyak alasan dan cara kami menggunakan selektor tulis ini dalam praktik.

Kita dapat membuat objek untuk mengkonsolidasikan pemilih baca dan penyeleksi tulis untuk memperbarui lebih sedikit tempat, tetapi bagaimana jika kita harus menulis banyak pemilih untuk beberapa halaman kita yang lebih kompleks? Ini bisa lebih rawan kesalahan untuk dibangun sendiri, jadi mari kita buat fungsi untuk dengan mudah menghasilkan pemilih baca ini dan pemilih tulis untuk akhirnya diekspor ke halaman tertentu.

Untuk fungsi generator pemilih baca, kita akan mengulang melalui properti objek input dan membentuk string pemilih CSS [data-hook=”selector”] untuk setiap nama elemen. Jika nilai kunci yang sesuai adalah null , kami akan menganggap nama elemen di objek input akan sama dengan nilai “data-hook” seperti { someElement: null } => { someElement: '[data-hook=”someElement”] } . Jika tidak, jika nilai kunci yang sesuai bukan null, kita dapat memilih untuk mengganti nilai “data-hook” seperti { someElement: “newSelector” } => { someElement: '[data-hook=”newSelector”]' } .

Untuk fungsi generator selektor tulis, kita akan mengulang melalui properti objek input dan membentuk objek { “data-hook”: “selector” } untuk setiap nama elemen. Jika nilai kunci yang sesuai adalah null , kami akan menganggap nama elemen di objek input akan sama dengan nilai "data-hook" seperti { someElement: null } => { someElement: { “data-hook”: “someElement” } } . Jika tidak, jika nilai kunci yang sesuai bukan null , kita dapat memilih untuk mengganti nilai "data-hook" seperti { someElement: “newSelector” } => { someElement: { “data-hook”: “newSelector” }' } .

Dengan menggunakan dua fungsi generator tersebut, kita membangun pemilih baca dan objek pemilih tulis untuk halaman dan mengekspornya untuk digunakan kembali dalam pengujian unit dan objek halaman Cypress. Bonus lainnya adalah objek-objek ini diketik sedemikian rupa sehingga kita tidak dapat secara tidak sengaja melakukan Selectors.unknownElement atau WriteSelectors.unknownElement dalam file TypeScript kita juga. Sebelum mengekspor pemilih baca kami, kami juga mengizinkan penambahan elemen tambahan dan pemetaan pemilih CSS untuk komponen pihak ketiga yang tidak kami kendalikan. Dalam beberapa kasus, kita tidak dapat menambahkan atribut “data-hook” ke elemen tertentu, jadi kita masih perlu memilih dengan atribut lain, id, dan kelas dan menambahkan lebih banyak properti ke objek pemilih baca seperti yang ditunjukkan di bawah ini.

Pola ini membantu kita untuk tetap teratur dengan semua pemilih kita untuk halaman dan ketika kita perlu memperbarui sesuatu. Kami menyarankan Anda menyelidiki cara untuk mengelola semua pemilih ini dalam beberapa jenis objek atau melalui cara lain apa pun untuk meminimalkan berapa banyak file yang perlu Anda sentuh pada perubahan di masa mendatang.

Mengkonsolidasikan batas waktu

Satu hal yang kami perhatikan setelah menulis banyak tes Cypress adalah bahwa penyeleksi kami untuk elemen tertentu akan kehabisan waktu dan membutuhkan waktu lebih lama dari nilai batas waktu default yang ditetapkan di cypress.json kami seperti "defaultCommandTimeout" dan "responseTimeout". Kami akan kembali dan menyesuaikan halaman tertentu yang membutuhkan waktu tunggu lebih lama, tetapi kemudian seiring waktu, jumlah nilai waktu tunggu yang berubah-ubah bertambah dan mempertahankannya menjadi lebih sulit untuk perubahan skala besar.

Akibatnya, kami mengkonsolidasikan batas waktu kami dalam objek yang dimulai di atas "defaultCommandTimeout" kami, yang berada di suatu tempat dalam rentang lima hingga sepuluh detik untuk mencakup sebagian besar batas waktu umum untuk penyeleksi kami seperti cy.get(...) atau cy.contains(...) . Di luar batas waktu default itu, kami akan meningkatkan ke "pendek", "sedang", "panjang", "xlong", dan "xxlong" dalam objek batas waktu yang dapat kami impor di mana saja di file kami untuk digunakan dalam perintah Cypress kami seperti cy.get(“someElement”, { timeout: timeouts.short }) or cy.task('pluginName', {}, { timeout: timeouts.xlong }) . Setelah mengganti batas waktu kami dengan nilai-nilai ini dari objek yang kami impor, kami memiliki satu tempat untuk memperbarui untuk meningkatkan atau mengurangi waktu yang diperlukan untuk batas waktu tertentu.

Contoh bagaimana Anda dapat dengan mudah memulai dengan ini ditunjukkan di bawah ini.

Membungkus

Saat rangkaian pengujian Cypress Anda berkembang, Anda mungkin mengalami beberapa masalah yang sama seperti yang kami alami saat mencari cara terbaik untuk menskalakan dan memelihara pengujian Cypress Anda. Anda dapat memilih untuk mengatur file Anda menurut halaman, fitur, atau beberapa konvensi pengelompokan lainnya, sehingga Anda selalu tahu di mana mencari file pengujian yang ada dan di mana menambahkan yang baru karena lebih banyak pengembang berkontribusi ke basis kode Anda.

Saat UI berubah, Anda dapat menggunakan beberapa jenis objek yang diketik (seperti pemilih baca dan tulis) untuk mempertahankan pemilih atribut "data-" Anda ke elemen kunci setiap halaman yang ingin Anda tegaskan atau interaksikan dalam pengujian unit Anda dan Cypress tes. Jika Anda mulai menerapkan terlalu banyak nilai arbitrer untuk hal-hal seperti nilai batas waktu untuk perintah Cypress Anda, mungkin sudah waktunya untuk menyiapkan objek yang diisi dengan nilai yang diskalakan sehingga Anda dapat memperbarui nilai tersebut di satu tempat.

Saat hal-hal berubah di seluruh UI frontend, API backend, dan pengujian Cypress, Anda harus selalu memikirkan cara untuk mempertahankan pengujian ini dengan lebih mudah. Lebih sedikit tempat untuk memperbarui dan membuat kesalahan dan lebih sedikit ambiguitas tentang di mana harus meletakkan sesuatu membuat perbedaan besar karena banyak pengembang menambahkan halaman baru, fitur, dan (pasti) tes Cypress di jalan.

Tertarik dengan lebih banyak posting di Cypress? Simak artikel-artikel berikut ini:

  • Apa yang Harus Dipertimbangkan Saat Menulis Tes E2E
  • 1.000 Kaki Ikhtisar Menulis Tes Cypress
  • TypeScript Semua Hal dalam Tes Cypress Anda
  • Berurusan Dengan Arus Email dalam Tes Cypress
  • Mengintegrasikan Pengujian Cypress Dengan Docker, Buildkite, dan CICD