Keinginan untuk menjauh dari pengujian regresi manual adalah alasan bagus untuk memperkenalkan autotest. Pertanyaannya adalah yang mana? Pengembang antarmuka Natalya Stus dan Alexei Androsov mengingat bagaimana tim mereka melewati beberapa iterasi dan membangun pengujian frontend di Auto.ru berdasarkan Jest dan Puppeteer: tes unit, tes untuk masing-masing komponen Bereaksi, tes integrasi. Yang paling menarik dari pengalaman ini adalah pengujian terisolasi komponen-React di browser tanpa Selenium Grid, Java, dan hal-hal lainnya.

Alexey:
- Pertama, Anda perlu memberi tahu sedikit apa itu Berita Otomotif. Ini adalah situs yang menjual mobil. Ada pencarian, akun pribadi, layanan mobil, suku cadang, ulasan, dealer dan banyak lagi. Auto.ru adalah proyek yang sangat besar, banyak kode. Kami menulis semua kode dalam monorepe besar, karena semuanya tercampur. Orang yang sama melakukan tugas serupa, misalnya, untuk seluler dan desktop. Ternyata banyak kode, dan monorepa sangat penting bagi kami. Pertanyaannya adalah bagaimana cara mengujinya?

Kami memiliki Bereaksi dan Node.js, yang melakukan rendering sisi server dan meminta data dari backend. Potongan-potongan yang tersisa dan kecil di BEM.

Natalya:
- Kami mulai berpikir ke arah otomatisasi. Siklus rilis aplikasi individual kami mencakup beberapa langkah. Pertama, fitur ini dikembangkan oleh programmer di cabang terpisah. Setelah itu, di cabang terpisah yang sama, fitur diuji oleh penguji manual. Jika semuanya baik-baik saja, tugas jatuh ke kandidat pelepasan. Jika tidak, maka kembali ke iterasi pengembangan lagi, tes lagi. Sampai penguji mengatakan bahwa semuanya baik-baik saja dalam fitur ini, itu tidak akan jatuh ke kandidat rilis.
Setelah mengumpulkan kandidat rilis, ada regresi manual - tidak hanya Auto.ru, tetapi hanya paket yang akan kami roll. Misalnya, jika kita akan memutar web desktop, maka ada regresi manual dari web desktop. Ini adalah banyak kasus uji manual. Regresi seperti itu memakan waktu sekitar satu hari kerja dari satu penguji manual.
Ketika regresi selesai, rilis terjadi. Setelah itu, cabang rilis bergabung ke master. Pada titik ini, kita bisa menyuntikkan kode master, yang kami uji hanya untuk web desktop, dan kode ini dapat merusak web seluler, misalnya. Ini tidak diperiksa segera, tetapi hanya pada regresi manual berikutnya - web seluler.

Secara alami, tempat paling menyakitkan dalam proses ini adalah regresi manual, yang memakan waktu sangat lama. Semua penguji manual, secara alami, lelah melakukan hal yang sama setiap hari. Karena itu, kami memutuskan untuk mengotomatiskan semuanya. Solusi pertama yang dieksekusi adalah tes mandiri Selenium dan Java, yang ditulis oleh tim terpisah. Ini adalah tes ujung ke ujung, e2e, yang menguji seluruh aplikasi. Mereka menulis sekitar 5 ribu tes semacam itu. Apa yang akhirnya kita lakukan?
Secara alami, kami mempercepat regresi. Tes otomatis lulus jauh lebih cepat daripada penguji manual, sekitar 10 kali lebih cepat hasilnya. Dengan demikian, tindakan rutin yang mereka lakukan setiap hari dihapus dari penguji manual. Bug yang ditemukan dari autotest lebih mudah direproduksi. Mulai ulang tes ini atau lihat langkah-langkahnya - tidak seperti penguji manual, yang akan berkata: "Saya mengklik sesuatu dan semuanya rusak."
Memberikan stabilitas lapisan. Kami selalu menjalankan uji coba yang sama - berbeda, sekali lagi, dari pengujian manual, ketika tester dapat mempertimbangkan bahwa kami tidak menyentuh tempat ini, dan saya tidak akan memeriksanya kali ini. Kami menambahkan pengujian untuk membandingkan tangkapan layar, meningkatkan akurasi pengujian UI - sekarang kami memeriksa perbedaan dalam beberapa piksel yang tidak akan dilihat oleh penguji dengan matanya. Semua berkat tes tangkapan layar.
Tapi ada kontra. Yang terbesar - untuk pengujian e2e kita membutuhkan lingkungan pengujian yang sepenuhnya konsisten dengan produk. Itu harus selalu diperbarui dan operasional. Ini membutuhkan kekuatan yang hampir sama banyaknya dengan dukungan stabilitas penjualan. Secara alami, kita tidak selalu mampu membelinya. Oleh karena itu, kami sering mengalami situasi di mana lingkungan pengujian terletak atau di suatu tempat ada sesuatu yang rusak, dan pengujian gagal, meskipun tidak ada masalah di paket paling depan.
Tes-tes ini juga sedang dikembangkan oleh tim terpisah, yang memiliki tugasnya sendiri, gilirannya sendiri dalam pelacak tugas, dan fitur-fitur baru ditutupi dengan beberapa penundaan. Mereka tidak dapat datang segera setelah rilis fitur baru dan segera menulis tanda kutip di atasnya. Karena tes mahal dan sulit untuk menulis dan memelihara, kami tidak mencakup semua skenario dengan mereka, tetapi hanya yang paling kritis. Pada saat yang sama, sebuah tim yang terpisah diperlukan, dan ia akan memiliki alat yang terpisah, infrastruktur yang terpisah, semua miliknya sendiri. Dan analisis tes jatuh juga merupakan tugas yang tidak sepele untuk penguji manual atau untuk pengembang. Saya akan menunjukkan beberapa contoh.

Kami telah menjalankan tes. 500 tes berlalu, beberapa jatuh. Kita bisa melihat hal seperti itu di laporan. Di sini tes tidak dimulai, dan tidak jelas apakah semuanya baik di sana atau tidak.

Contoh lain - tes dimulai, tetapi jatuh dengan kesalahan seperti itu. Dia tidak dapat menemukan elemen di halaman, tetapi mengapa - kita tidak tahu. Entah elemen ini tidak muncul, atau ternyata berada di halaman yang salah, atau pelacak berubah. Yang Anda butuhkan untuk pergi dan debazh tangan.

Tes tangkapan layar juga tidak selalu memberi kita akurasi yang baik. Di sini kita memuat beberapa jenis kartu, kartu itu sedikit bergerak, tes kami telah jatuh.

Kami mencoba memecahkan sejumlah masalah ini. Kami mulai menjalankan bagian dari tes pada prod - tes yang tidak memengaruhi data pengguna tidak mengubah apa pun dalam database. Artinya, kami di prod membuat mesin terpisah yang melihat ke lingkungan prod. Kami baru saja menginstal paket frontend baru dan menjalankan tes di sana. Produk setidaknya stabil.
Kami mentransfer beberapa tes ke mokeys, tetapi kami memiliki banyak backend yang berbeda, API yang berbeda, dan mengunci semuanya adalah tugas yang sangat sulit, terutama untuk 5 ribu tes. Untuk ini, layanan khusus yang disebut mockritsa ditulis, ini membantu membuat mokas yang diperlukan untuk frontend cukup mudah dan cukup mudah untuk mem-proxy-nya.
Kami juga harus membeli seterika agar kisi-kisi Selenium kami yang darinya pengujian ini diluncurkan lebih besar sehingga tidak akan jatuh, karena mereka tidak dapat menaikkan peramban, dan karenanya, akan berjalan lebih cepat. Bahkan setelah kami mencoba menyelesaikan masalah ini, kami masih sampai pada kesimpulan bahwa tes seperti itu tidak cocok untuk CI, mereka membutuhkan waktu yang sangat lama. Kami tidak dapat menjalankannya pada setiap permintaan kumpulan. Kami hanya tidak pernah dalam hidup kami nanti akan menganalisis laporan ini, yang akan dihasilkan untuk setiap permintaan kumpulan.

Oleh karena itu, untuk CI, kita memerlukan tes cepat dan stabil yang tidak akan gagal karena beberapa alasan acak. Kami ingin menjalankan tes untuk permintaan kumpulan tanpa dudukan uji, backend, database, tanpa kasus pengguna yang rumit.
Kami ingin tes-tes ini ditulis bersamaan dengan kode, dan bahwa hasil tes segera memperjelas jika ada masalah.
Alexey:
- Ya, dan kami memutuskan untuk mencoba semua yang kami inginkan, untuk meluruskan semuanya dari awal hingga akhir dalam infrastruktur Jest yang sama. Mengapa kami memilih Jest? Kami sudah menulis unit test pada Jest, kami menyukainya. Ini adalah alat populer yang didukung, ia sudah memiliki banyak integrasi yang sudah jadi di sana: Bereaksi tes render, Enzyme. Semuanya berfungsi di luar kotak, tidak ada yang perlu dibangun, semuanya sederhana.

Dan Jest secara pribadi menang untuk saya dalam hal itu, tidak seperti moka apa pun, sulit untuk menembakkan efek samping dari semacam tes pihak ketiga di kaki Anda jika saya lupa membersihkannya atau yang lainnya. Dalam moka, ini dilakukan sekali atau dua kali, tetapi di Jest sulit untuk melakukannya: itu terus diluncurkan di utas terpisah. Itu mungkin, tetapi sulit. Dan untuk e2e yang dirilis Puppeteer, kami juga memutuskan untuk mencobanya. Itu yang kita dapat.

Natalya:
"Aku juga akan mulai dengan contoh unit test." Ketika kami menulis tes hanya untuk beberapa fungsi, tidak ada masalah khusus. Kami menyebut fungsi ini, memberikan beberapa argumen, membandingkan apa yang terjadi dengan apa yang seharusnya terjadi.
Jika kita berbicara tentang komponen-React, maka semuanya menjadi sedikit lebih rumit. Kita perlu membuat mereka entah bagaimana. Ada penyaji uji Bereaksi, tetapi tidak terlalu nyaman untuk uji unit, karena tidak akan memungkinkan kami untuk menguji komponen secara terpisah. Ini akan membuat komponen sepenuhnya ke ujung, ke tata letak.
Dan saya ingin menunjukkan bagaimana dengan Enzim dimungkinkan untuk menulis unit test untuk komponen Bereaksi menggunakan contoh komponen seperti itu di mana kita memiliki MyComponent tertentu. Dia mendapat semacam penyangga, dia punya semacam logika. Lalu dia mengembalikan komponen Foo, yang, pada gilirannya, akan mengembalikan komponen bar, yang sudah di komponen bar kembali kepada kita, pada kenyataannya, tata letak.

Kita dapat menggunakan alat Enzyme seperti rendering dangkal. Inilah yang kita butuhkan untuk menguji komponen MyComponent secara terpisah. Dan tes-tes ini tidak akan tergantung pada komponen apa yang akan terkandung di dalamnya. Kami hanya akan menguji logika komponen MyComponent.
Jest memiliki sesuatu yang disebut Snapshot, dan mereka juga dapat membantu kita di sini. “Expect something toMatchSnapshot” akan membuat struktur seperti itu bagi kami, hanya file teks yang menyimpan, pada kenyataannya, apa yang kami lewati untuk diharapkan, apa yang terjadi, dan ketika tes ini dijalankan untuk pertama kalinya, file ini ditulis. Dengan menjalankan tes lebih lanjut, apa yang diperoleh akan dibandingkan dengan standar yang terkandung dalam file MyComponent.test.js.snap.
Di sini kita melihat bahwa keseluruhan render, mengembalikan persis apa yang dirender metode dari MyComponent, dan apa foo, secara umum, tidak peduli. Kita dapat menulis dua tes seperti itu untuk dua kasus kita, untuk dua kasus kita untuk komponen MyComponent.

Pada prinsipnya, kita dapat menguji hal yang sama tanpa Snapshot, cukup memeriksa skrip yang kita butuhkan, misalnya, memeriksa prop mana yang diteruskan ke komponen foo. Tetapi pendekatan ini memiliki satu minus. Jika kami menambahkan beberapa elemen lain ke MyComponent, pengujian baru kami, ini tidak akan ditampilkan dengan cara apa pun.

Karena itu, bagaimanapun juga, tes Snapshot adalah tes yang akan menunjukkan kepada kita hampir semua perubahan di dalam komponen. Tetapi jika kita menulis kedua tes pada Snapshot, dan kemudian kita membuat perubahan yang sama pada komponen, maka kita akan melihat bahwa kedua tes akan jatuh. Pada prinsipnya, hasil tes jatuh ini akan memberi tahu kita tentang hal yang sama, bahwa kita menambahkan semacam "halo" di sana.

Dan ini juga berlebihan, oleh karena itu, saya percaya bahwa lebih baik menggunakan satu tes Snapshot untuk struktur yang sama. Periksa sisa logikanya dengan cara yang berbeda, tanpa Snapshot, karena Snapshot, mereka tidak terlalu indikatif. Ketika Anda melihat Snapshot, Anda baru saja melihat bahwa sesuatu telah dirender, tetapi tidak jelas logika mana yang Anda uji di sini. Ini sama sekali tidak cocok untuk TDD jika Anda ingin menggunakannya. Dan itu tidak akan berfungsi seperti dokumentasi. Maka, ketika Anda melihat komponen ini, Anda akan melihat bahwa ya, Snapshot berhubungan dengan sesuatu, tetapi jenis logika apa yang ada di sana tidak begitu jelas.


Dengan cara yang sama, kami akan menulis unit test pada komponen foo, pada komponen bar, misalnya, Snapshot.

Kami mendapatkan cakupan 100% untuk ketiga komponen ini. Kami percaya bahwa kami telah memeriksa semuanya, kami selesai dengan baik.
Tetapi katakanlah kita mengubah sesuatu di komponen bar, menambahkan beberapa prop baru ke dalamnya, dan kami memiliki tes untuk komponen bar, jelas. Kami mengoreksi tes, dan ketiga tes lulus bersama kami.

Tetapi pada kenyataannya, jika kita mengumpulkan seluruh cerita ini, maka tidak ada yang akan berhasil, karena MyComponent tidak akan menyatu dengan kesalahan seperti itu. Kami tidak benar-benar melewati prop yang diharapkan ke komponen bar. Oleh karena itu, kita berbicara tentang fakta bahwa dalam kasus ini kita juga memerlukan tes integrasi yang akan memeriksa, termasuk apakah kita memanggil komponen anaknya dengan benar dari komponen kita.

Memiliki komponen seperti itu dan mengubah salah satunya, Anda segera melihat perubahan apa yang terjadi pada komponen ini.
Peluang apa yang kami miliki di Enzim untuk melakukan pengujian integrasi? Render dangkal itu sendiri mengembalikan struktur seperti itu. Ini memiliki metode menyelam di dalamnya, jika dipanggil pada beberapa komponen Bereaksi, itu akan gagal di dalamnya. Dengan demikian, dengan menyebutnya pada komponen foo, kita mendapatkan apa yang dirender komponen foo, ini adalah bar, jika kita melakukan penyelaman lagi, kita akan mendapatkan, pada kenyataannya, tata letak yang dikembalikan komponen bar kepada kita. Ini hanya akan menjadi tes integrasi.

Atau Anda dapat merender semuanya sekaligus menggunakan metode mount, yang mengimplementasikan render DOM penuh. Tapi saya tidak menyarankan melakukan ini, karena itu akan menjadi Snapshot yang sangat sulit. Dan, sebagai aturan, Anda tidak perlu memeriksa seluruh struktur sepenuhnya. Anda hanya perlu memeriksa integrasi antara komponen induk dan anak dalam setiap kasus.

Dan untuk MyComponent kami menambahkan tes integrasi, jadi pada tes pertama saya menambahkan hanya menyelam, dan ternyata kami menguji tidak hanya logika komponen itu sendiri, tetapi juga integrasi dengan komponen foo. Hal yang sama, kami menambahkan tes integrasi untuk komponen foo yang benar memanggil komponen bar, dan kemudian kami memeriksa seluruh rantai ini, dan kami yakin bahwa tidak ada perubahan yang akan menghancurkan kami, pada kenyataannya, rendering dari MyComponent

Contoh lain, sudah dari proyek nyata. Secara singkat tentang apa lagi yang bisa dilakukan Jest dan Enzim. Jest bisa melakukan moki. Anda dapat, jika Anda menggunakan beberapa fungsi eksternal dalam komponen Anda, Anda dapat menguncinya. Misalnya, dalam contoh ini, kami memanggil beberapa jenis api, kami tentu saja tidak ingin masuk ke api apa pun di unit test, jadi kami hanya menghapus fungsi getResource dengan beberapa objek jest.fn. Bahkan, fungsi tiruannya. Kemudian kita dapat memeriksa apakah itu dipanggil atau tidak, berapa kali dipanggil, dengan argumen apa. Semua ini memungkinkan Anda untuk melakukan Jest.

Dalam rendering dangkal, Anda bisa meneruskan store ke komponen. Jika Anda membutuhkan toko, Anda bisa memindahkannya ke sana, dan itu akan berhasil.

Anda juga dapat mengubah Status dan penyangga dalam komponen yang sudah dirender.

Anda dapat memanggil metode simulasi pada beberapa komponen. Itu hanya memanggil pawang. Misalnya, jika Anda mensimulasikan klik, itu akan memanggil onClick untuk komponen tombol di sini. Semua ini dapat dibaca, tentu saja, dalam dokumentasi tentang Enzim, banyak potongan berguna. Ini hanya beberapa contoh dari proyek nyata.

Alexey:
- Kami sampai pada pertanyaan yang paling menarik. Kita dapat menguji Jest, kita dapat menulis unit test, memeriksa komponen, memeriksa elemen mana yang merespons klik dengan tidak benar. Kami dapat memeriksa html mereka. Sekarang kita perlu memeriksa tata letak komponen, css.

Dan disarankan untuk melakukan ini sehingga prinsip pengujian tidak berbeda dengan yang saya jelaskan sebelumnya. Jika saya memeriksa html, maka saya menelepon rendering dangkal, butuh dan memberikan html kepada saya. Saya ingin memeriksa css, panggil saja semacam render dan periksa - tanpa mengangkat apa pun, tanpa menyiapkan alat apa pun.

Saya mulai mencarinya, dan hampir di mana-mana jawaban yang sama diberikan untuk semua yang disebut Puppeteer, atau grid Selenium. Anda membuka beberapa tab, Anda pergi ke html halaman, mengambil screenshot dan membandingkannya dengan opsi sebelumnya. Jika belum berubah, maka semuanya baik-baik saja.
Pertanyaannya adalah, apa itu html halaman jika saya hanya ingin memeriksa satu komponen secara terpisah? Diinginkan - dalam kondisi yang berbeda.

Saya tidak ingin menulis banyak html halaman ini untuk setiap komponen, untuk setiap negara. Avito berjalan dengan baik. Roma Dvornov menerbitkan sebuah artikel tentang Habré, dan dia, dengan cara, berpidato. Apa yang mereka lakukan Mereka mengambil komponen, merakit html melalui render standar. Kemudian dengan bantuan plugin dan segala macam trik mereka mengumpulkan semua aset yang mereka miliki - gambar, css. Masukkan semuanya ke dalam html, dan mereka hanya mendapatkan html yang tepat.

Dan kemudian mereka mengangkat server khusus, mengirim html di sana, menjadikannya, dan mengembalikan beberapa hasil. Artikel yang sangat menarik, baca, namun, Anda dapat menarik banyak ide menarik dari sana.

Apa yang saya tidak suka di sana. Merakit komponen berbeda dari cara kerjanya menuju produksi. Sebagai contoh, kami memiliki webpack, dan di sana ia akan dikumpulkan oleh beberapa jenis aset babel, itu ditarik secara berbeda di sana. Saya tidak dapat menjamin bahwa saya menguji apa yang akan saya unduh sekarang.
Dan lagi, layanan terpisah untuk tangkapan layar. Saya ingin melakukannya dengan lebih mudah. Dan ternyata, ada ide bahwa, mari kita kumpulkan persis sama seperti yang akan kita kumpulkan. Dan cobalah untuk menggunakan sesuatu seperti Docker, karena itu adalah hal seperti itu, dapat diletakkan di komputer, secara lokal, itu akan menjadi sederhana, terisolasi, tidak menyentuh apa pun, semuanya baik-baik saja.

Tapi masalah ini adalah dengan html halaman, tetap sama seperti apa sebenarnya. Dan sebuah ide lahir. Anda memiliki webpack.conf yang disederhanakan, dan darinya ada beberapa EntryPoint untuk klien js. Modul-modul tersebut dijelaskan, cara merakitnya, file output, semua plugin yang telah Anda jelaskan, semuanya sudah dikonfigurasi, semuanya baik-baik saja.

Bagaimana jika saya suka ini? Dia akan masuk ke komponen saya dan mengumpulkannya secara terpisah. Dan akan ada tepat satu komponen. Jika saya menambahkan html webpack di sana, itu juga akan memberi saya html, dan aset ini akan dikumpulkan di sana, dan hal ini, bagaimanapun, sudah dapat diuji secara otomatis.
Dan saya akan menulis semua ini, tetapi kemudian saya menemukan ini.

Jest-puppeteer-React, sebuah plugin muda. Dan saya mulai aktif berkontribusi untuk itu. Jika Anda tiba-tiba ingin mencobanya, Anda dapat, misalnya, datang kepada saya, entah bagaimana saya dapat membantu. Sebenarnya, proyek itu bukan milikku.
Anda menulis file biasa sebagai test.js, dan file-file ini perlu ditulis sedikit secara terpisah untuk membantu menemukannya, agar tidak mengkompilasi seluruh proyek untuk Anda, tetapi untuk mengkompilasi hanya komponen yang diperlukan. Bahkan, Anda mengambil konfigurasi webpack. Dan titik input berubah ke file browser.js ini, yaitu, persis apa yang ingin kami uji akan dikemas dalam html, dan dengan bantuan Puppeteer itu akan membawa Anda tangkapan layar.

Apa yang bisa dia lakukan? , jest-image-snapshot. . , , js, media-query, , .
headless-, , , , , headless-, Chrome . web-, , , , .
Docker. . . , Docker, . . Docker , , , Linux, - , - . Docker , .

? , . , . before-after, , . , . , Chrome, Firefox. .
. pixelmatch. , looksame, «», . , .

— . , , . , : - , — Enzyme. Redux store . . viewport, , . , , .

. , . ? , .

: 5-10 . Selenium . , , , . .

Puppeteer, e2e-. , e2e- — , Selenium.
:
— , Selenium Java , . - JS Puppeteer, , .
, . , , .

— Selenium Java, — JS Puppeteer. . 18 . , , Java. , , Java Selenium.

:
— ? . , html-, css . e2e. . , .

, , . . , , — , . , . - , , : , .
, , . git hook, -, . green master — , , , . Terima kasih