Picasso: Cara Menguji Pustaka Komponen
Diterbitkan: 2022-03-11Versi baru dari sistem desain Toptal dirilis baru-baru ini yang mengharuskan kami membuat perubahan pada hampir setiap komponen di Picasso, pustaka komponen internal kami. Tim kami menghadapi tantangan: Bagaimana kami memastikan bahwa regresi tidak terjadi?
Jawaban singkatnya adalah, agak tidak mengejutkan, tes. Banyak tes.
Kami tidak akan meninjau aspek teoretis pengujian atau mendiskusikan berbagai jenis pengujian, kegunaannya, atau menjelaskan mengapa Anda harus menguji kode Anda sejak awal. Blog kami dan lainnya telah membahas topik tersebut. Sebaliknya, kami hanya akan fokus pada aspek praktis pengujian.
Baca terus untuk mengetahui bagaimana pengembang di Toptal menulis tes. Repositori kami bersifat publik, jadi kami menggunakan contoh dunia nyata. Tidak ada abstraksi atau penyederhanaan.
Uji Piramida
Kami tidak memiliki piramida pengujian yang ditentukan, tetapi jika kami melakukannya, itu akan terlihat seperti ini:
Piramida pengujian Toptal menggambarkan pengujian yang kami tekankan.
Tes Unit
Tes unit mudah untuk ditulis dan mudah dijalankan. Jika Anda memiliki sedikit waktu untuk menulis tes, itu harus menjadi pilihan pertama Anda.
Namun, mereka tidak sempurna. Terlepas dari perpustakaan pengujian mana yang Anda pilih (Jest and React Testing Library [RTL] dalam kasus kami), itu tidak akan memiliki DOM nyata dan tidak akan memungkinkan Anda untuk memeriksa fungsionalitas di browser yang berbeda, tetapi itu akan memungkinkan Anda untuk menghapus menghilangkan kerumitan dan menguji blok bangunan sederhana dari perpustakaan Anda.
Pengujian unit tidak hanya menambah nilai dengan menguji perilaku kode, tetapi juga dengan memeriksa keseluruhan pengujian kode. Jika Anda tidak dapat menulis tes unit dengan mudah, kemungkinan Anda memiliki kode yang buruk.
Tes Regresi Visual
Bahkan jika Anda memiliki cakupan pengujian unit 100%, itu tidak berarti komponen terlihat bagus di seluruh perangkat dan browser.
Regresi visual sangat sulit dikenali dengan pengujian manual. Misalnya, jika label tombol dipindahkan sebesar 1 piksel, apakah teknisi QA akan menyadarinya? Untungnya, ada banyak solusi untuk masalah visibilitas terbatas ini. Anda dapat memilih solusi all-in-one tingkat perusahaan seperti LambdaTest atau Mabl. Anda dapat memasukkan plugin, seperti Percy, ke dalam pengujian yang ada, serta solusi DIY dari orang-orang seperti Loki atau Storybook (yang kami gunakan sebelum Picasso). Mereka semua memiliki kekurangan: Beberapa terlalu mahal sementara yang lain memiliki kurva belajar yang curam atau membutuhkan terlalu banyak perawatan.
Selamat untuk menyelamatkan! Ini adalah pesaing langsung Percy, tetapi jauh lebih murah, mendukung lebih banyak browser, dan lebih mudah digunakan. Nilai jual besar lainnya? Ini mendukung integrasi Cypress, yang penting karena kami ingin beralih dari menggunakan Storybook untuk pengujian visual. Kami menemukan diri kami dalam situasi di mana kami harus membuat cerita hanya agar kami dapat memastikan cakupan pengujian visual, bukan karena kami perlu mendokumentasikan kasus penggunaan itu. Itu mencemari dokumen kami dan membuatnya lebih sulit untuk dipahami. Kami ingin mengisolasi pengujian visual dari dokumentasi visual.
Tes Integrasi
Bahkan jika dua komponen memiliki pengujian unit dan visual, itu tidak menjamin bahwa mereka akan bekerja sama. Misalnya, kami menemukan bug di mana tooltip tidak terbuka saat digunakan dalam item dropdown namun berfungsi dengan baik saat digunakan sendiri.
Untuk memastikan komponen terintegrasi dengan baik, kami menggunakan fitur pengujian komponen eksperimental Cypress. Pada awalnya, kami tidak puas dengan kinerja yang buruk, tetapi kami dapat meningkatkannya dengan konfigurasi webpack khusus. Hasil? Kami dapat menggunakan API Cypress yang sangat baik untuk menulis pengujian kinerja yang memastikan komponen kami bekerja sama dengan baik.
Menerapkan Piramida Pengujian
Seperti apa semua ini dalam kehidupan nyata? Mari kita uji komponen Accordion!
Naluri pertama Anda mungkin membuka editor Anda dan mulai menulis kode. Saranku? Luangkan waktu untuk memahami semua fitur komponen dan tuliskan kasus uji apa yang ingin Anda bahas.
Apa yang Harus Diuji?
Berikut rincian kasus yang harus dicakup oleh pengujian kami:
- Status – Akordeon dapat diperluas dan diciutkan, status defaultnya dapat dikonfigurasi, dan fitur ini dapat dinonaktifkan
- Gaya – Akordeon dapat memiliki variasi batas
- Konten – Mereka dapat berintegrasi dengan unit perpustakaan lainnya
- Kustomisasi – Komponen dapat diganti gayanya dan dapat memiliki ikon perluasan kustom
- Callback – Setiap kali status berubah, callback dapat dipanggil
Bagaimana Menguji?
Sekarang kita tahu apa yang harus kita uji, mari kita pertimbangkan bagaimana melakukannya. Kami memiliki tiga opsi dari piramida pengujian kami. Kami ingin mencapai cakupan maksimum dengan tumpang tindih minimal antara bagian piramida. Apa cara terbaik untuk menguji setiap kasus uji?
- Status – Pengujian unit dapat membantu kami menilai apakah status berubah sesuai, tetapi kami juga memerlukan pengujian visual untuk memastikan komponen dirender dengan benar di setiap status
- Gaya – Tes visual cukup untuk mendeteksi regresi dari varian yang berbeda
- Konten – Kombinasi tes visual dan integrasi adalah pilihan terbaik, karena Akordeon dapat digunakan dalam kombinasi dengan banyak komponen lainnya
- Kustomisasi – Kami dapat menggunakan pengujian unit untuk memverifikasi apakah nama kelas diterapkan dengan benar, tetapi kami memerlukan pengujian visual untuk memastikan bahwa komponen dan gaya kustom bekerja bersama-sama
- Callback – Tes unit ideal untuk memastikan callback yang tepat dipanggil
Piramida Pengujian Akordeon
Tes Unit
Rangkaian lengkap unit test dapat ditemukan di sini. Kami telah membahas semua perubahan status, penyesuaian, dan panggilan balik:

it('toggles', async () => { const handleChange = jest.fn() const { getByText, getByTestId } = renderAccordion({ onChange: handleChange, expandIcon: <span data-test /> }) fireEvent.click(getByTestId('accordion-summary')) await waitFor(() => expect(getByText(DETAILS_TEXT)).toBeVisible()) fireEvent.click(getByTestId('trigger')) await waitFor(() => expect(getByText(DETAILS_TEXT)).not.toBeVisible()) fireEvent.click(getByText(SUMMARY_TEXT)) await waitFor(() => expect(getByText(DETAILS_TEXT)).toBeVisible()) expect(handleChange).toHaveBeenCalledTimes(3) })Tes Regresi Visual
Tes visual terletak di blok deskripsi Cypress ini. Tangkapan layar dapat ditemukan di dasbor Happo.
Anda dapat melihat semua status komponen, varian, dan penyesuaian yang berbeda telah direkam. Setiap kali PR dibuka, CI membandingkan tangkapan layar yang disimpan Happo dengan tangkapan layar yang diambil di cabang Anda:
it('renders', () => { mount( <TestingPicasso> <TestAccordion /> </TestingPicasso> ) cy.get('body').happoScreenshot() }) it('renders disabled', () => { mount( <TestingPicasso> <TestAccordion disabled /> <TestAccordion expandIcon={<Check16 />} /> </TestingPicasso> ) cy.get('body').happoScreenshot() }) it('renders border variants', () => { mount( <TestingPicasso> <TestAccordion borders='none' /> <TestAccordion borders='middle' /> <TestAccordion borders='all' /> </TestingPicasso> ) cy.get('body').happoScreenshot() })Tes Integrasi
Kami menulis tes "jalur buruk" di blok deskripsi Cypress ini yang menyatakan bahwa Accordion masih berfungsi dengan benar dan bahwa pengguna dapat berinteraksi dengan komponen khusus. Kami juga menambahkan pernyataan visual untuk kepercayaan diri ekstra:
describe('Accordion with custom summary', () => { it('closes and opens', () => { mount(<AccordionCustomSummary />) toggleAccordion() getAccordionContent().should('not.be.visible') cy.get('[data-testid=accordion-custom-summary]').happoScreenshot() toggleAccordion() getAccordionContent().should('be.visible') cy.get('[data-testid=accordion-custom-summary]').happoScreenshot() }) // … })Integrasi Berkelanjutan
Picasso hampir sepenuhnya bergantung pada GitHub Actions untuk QA. Selain itu, kami menambahkan kait Git untuk pemeriksaan kualitas kode file bertahap. Kami baru saja bermigrasi dari Jenkins ke GHA, jadi penyiapan kami masih dalam tahap MVP.
Alur kerja dijalankan pada setiap perubahan di cabang jarak jauh secara berurutan, dengan integrasi dan tes visual menjadi tahap terakhir karena paling mahal untuk dijalankan (baik dalam hal kinerja dan biaya moneter). Kecuali semua tes berhasil diselesaikan, permintaan tarik tidak dapat digabungkan.
Ini adalah tahapan yang dilalui GitHub Actions setiap saat:
- Instalasi ketergantungan
- Kontrol versi – Memvalidasi bahwa format komit dan judul PR cocok dengan komit konvensional
- Lint – ESlint memastikan kode berkualitas baik
- Kompilasi TypeScript – Pastikan tidak ada kesalahan ketik
- Kompilasi paket – Jika paket tidak dapat dibuat, maka paket tersebut tidak akan berhasil dirilis; tes Cypress kami juga mengharapkan kode yang dikompilasi
- Tes unit
- Integrasi dan tes visual
Alur kerja lengkap dapat ditemukan di sini. Saat ini, dibutuhkan kurang dari 12 menit untuk menyelesaikan semua tahapan.
Kemampuan untuk diuji
Seperti kebanyakan pustaka komponen, Picasso memiliki komponen root yang harus membungkus semua komponen lain dan dapat digunakan untuk menetapkan aturan global. Hal ini mempersulit penulisan pengujian karena dua alasan—inkonsistensi dalam hasil pengujian, bergantung pada properti yang digunakan dalam pembungkus; dan boilerplate tambahan:
import { render } from '@testing-library/react' describe('Form', () => { it('renders', () => { const { container } = render( <Picasso loadFavicon={false} environment='test'> <Form /> </Picasso> ) expect(container).toMatchSnapshot() }) })Kami memecahkan masalah pertama dengan membuat TestingPicasso yang mengkondisikan aturan global untuk pengujian. Tapi menjengkelkan harus mendeklarasikannya untuk setiap kasus uji. Itu sebabnya kami membuat fungsi render kustom yang membungkus komponen yang diteruskan dalam TestingPicasso dan mengembalikan semua yang tersedia dari fungsi render RTL.
Pengujian kami sekarang lebih mudah dibaca dan ditulis dengan mudah:
import { render } from '@toptal/picasso/test-utils' describe('Form', () => { it('renders', () => { const { container } = render(<Form />) expect(container).toMatchSnapshot() }) })Kesimpulan
Pengaturan yang dijelaskan di sini jauh dari sempurna, tetapi ini adalah titik awal yang baik bagi Anda yang cukup berani untuk membuat pustaka komponen. Saya telah membaca banyak tentang pengujian piramida, tetapi tidak selalu mudah untuk menerapkannya dalam praktik. Oleh karena itu, saya mengundang Anda untuk menjelajahi basis kode kami dan belajar dari kesalahan dan keberhasilan kami.
Pustaka komponen unik karena melayani dua jenis audiens: pengguna akhir yang berinteraksi dengan UI dan pengembang yang menggunakan kode Anda untuk membangun aplikasi mereka sendiri. Menginvestasikan waktu dalam kerangka pengujian yang kuat akan menguntungkan semua orang. Menginvestasikan waktu dalam peningkatan kemampuan pengujian akan menguntungkan Anda sebagai pengelola dan teknisi yang menggunakan (dan menguji) perpustakaan Anda.
Kami tidak membahas hal-hal seperti cakupan kode, pengujian ujung ke ujung, dan kebijakan versi dan rilis. Saran singkat tentang topik tersebut adalah: rilis sering, latih versi semantik yang tepat, memiliki transparansi dalam proses Anda, dan menetapkan harapan untuk para insinyur yang mengandalkan perpustakaan Anda. Kami dapat meninjau kembali topik ini secara lebih rinci di posting berikutnya.
