"Wow, aku tidak tahu itu!" saat-saat dengan bercanda

Halo semuanya! Kursus Pengembang JavaScript dimulai Kamis ini. Dalam hal ini, kami memutuskan untuk membagikan terjemahan materi menarik lainnya. Selamat membaca.



Jest selalu menjadi alat pengujian unit saya yang sangat diperlukan. Sangat dapat diandalkan sehingga saya mulai berpikir bahwa saya selalu kurang menggunakannya. Meskipun tes berhasil, lama-kelamaan saya refactored mereka di sana-sini, karena saya tidak tahu bahwa Jest bisa melakukan itu. Setiap kali ini adalah kode baru ketika saya memeriksa dokumentasi Jest.

Jadi, saya akan membagikan beberapa trik Jest favorit saya yang mungkin sudah diketahui oleh beberapa dari Anda karena Anda membaca dokumentasi dan tidak menyukai saya (malu), tetapi saya harap ini membantu mereka yang baru saja menjalankannya dengan cepat !

Omong-omong, saya menggunakan Jest v24.8.0 sebagai bahan referensi, jadi berhati-hatilah, beberapa hal mungkin tidak berfungsi pada versi Jest yang sedang Anda gunakan. Selain itu, contoh-contoh tidak mewakili kode tes yang sebenarnya, ini hanya sebuah demonstrasi.

# 1 .toBe vs .toEqual


Pada awalnya, semua pernyataan ini tampak normal bagi saya:

expect('foo').toEqual('foo') expect(1).toEqual(1) expect(['foo']).toEqual(['foo']) 

Berdasarkan penggunaan chai untuk pernyataan kesetaraan (to.equal), ini wajar saja. Bahkan, Jest tidak akan mengeluh, dan pernyataan ini berlalu seperti biasa.

Namun, Jest memiliki .toBe dan .toEqual. Yang pertama digunakan untuk menyatakan kesetaraan menggunakan Object.is , dan yang kedua adalah untuk memberikan perbandingan objek dan array yang mendalam. .toEqual memiliki fallback untuk menggunakan Object.is jika ternyata Anda tidak memerlukan perbandingan yang mendalam, seperti menyatakan persamaan untuk nilai primitif, yang menjelaskan mengapa contoh sebelumnya berjalan sangat baik.

 expect('foo').toBe('foo') expect(1).toBe(1) expect(['foo']).toEqual(['foo']) 

Dengan begitu, Anda bisa melewati semua if-else di .toEqual menggunakan .toBe jika Anda sudah tahu nilai apa yang Anda uji.
Kesalahan umum adalah bahwa Anda akan menggunakan .toBe untuk menegaskan kesetaraan nilai-nilai primitif.

 expect(['foo']).toBe(['foo']) 

Jika Anda melihat kode sumber saat .toBe lumpuh, ia akan mencoba menentukan apakah Anda benar-benar membuat kesalahan ini dengan memanggil fungsi yang .toEqual gunakan. Ini bisa menjadi hambatan ketika mengoptimalkan tes Anda.

Jika Anda yakin menggunakan nilai primitif, kode Anda dapat ditata ulang untuk tujuan pengoptimalan:

 expect(Object.is('foo', 'foo')).toBe(true) 

Lebih detail dalam dokumentasi .

# 2 Perbandingan yang lebih cocok


Secara teknis, Anda dapat menggunakan .toBe untuk memvalidasi nilai apa pun. Dengan Jest, Anda dapat secara khusus menggunakan alat perbandingan tertentu untuk membuat tes Anda lebih mudah dibaca (dan dalam beberapa kasus lebih pendek).

 // expect([1,2,3].length).toBe(3) // expect([1,2,3]).toHaveLength(3) const canBeUndefined = foo() // expect(typeof canBeUndefined !== 'undefined').toBe(true) // expect(typeof canBeUndefined).not.toBe('undefined') // expect(canBeUndefined).not.toBe(undefined) // expect(canBeUndefined).toBeDefined() class Foo { constructor(param) { this.param = param } // expect(new Foo('bar') instanceof Foo).toBe(true) // expect(new Foo('bar')).toBeInstanceOf(Foo) 

Ini hanya beberapa yang saya pilih dari daftar panjang kompiler Jest dalam dokumentasi, Anda dapat melihat sisanya sendiri.

# 3 Pengujian snapshot pada elemen tanpa antarmuka pengguna


Anda mungkin pernah mendengar tentang pengujian snapshot di Jest , yang membantu melacak perubahan pada elemen antarmuka pengguna Anda. Tetapi pengujian dengan snapshot tidak terbatas pada hal ini.

Pertimbangkan contoh ini:

 const allEmployees = getEmployees() const happyEmployees = giveIncrementByPosition(allEmployees) expect(happyEmployees[0].nextMonthPaycheck).toBe(1000) expect(happyEmployees[1].nextMonthPaycheck).toBe(5000) expect(happyEmployees[2].nextMonthPaycheck).toBe(4000) // ...etc 

Akan melelahkan jika Anda mengklaim semakin banyak karyawan. Selain itu, jika ternyata lebih banyak klaim diperlukan untuk setiap karyawan, kalikan jumlah klaim baru dengan jumlah karyawan dan Anda mendapatkan ide.
Dengan pengujian snapshot, semua ini bisa dilakukan hanya seperti ini:

 const allEmployees = getEmployees() const happyEmployees = giveIncrementByPosition(allEmployees) expect(happyEmployees).toMatchSnapshot() 

Setiap kali regresi terjadi, Anda akan tahu persis pohon mana di simpul yang tidak cocok dengan gambar.

Tetapi kenyamanan ada harganya: metode ini lebih rentan kesalahan. Ada kemungkinan Anda tidak akan tahu bahwa gambar itu sebenarnya salah, dan pada akhirnya Anda akan memotretnya. Jadi periksa ulang snapshot Anda seolah-olah itu adalah kode persetujuan Anda sendiri (karena memang begitu).

Tentu saja, pengujian tidak terbatas pada snapshot. Baca dokumentasi lengkap.

# 4 deskripsikan. masing-masing dan uji


Pernahkah Anda menulis tes yang agak mirip dengan ini?

 describe('When I am a supervisor', () => { test('I should have a supervisor badge', () => { const employee = new Employee({ level: 'supervisor' }) expect(employee.badges).toContain('badge-supervisor') }) test('I should have a supervisor level', () => { const employee = new Employee({ level: 'supervisor' }) expect(employee.level).toBe('supervisor') }) }) describe('When I am a manager', () => { test('I should have a manager badge', () => { const employee = new Employee({ level: 'manager' }) expect(employee.badges).toContain('badge-manager') }) test('I should have a manager level', () => { const employee = new Employee({ level: 'manager' }) expect(employee.level).toBe('manager') }) }) 

Ini monoton dan rutin, bukan? Bayangkan melakukan ini dengan banyak kasus.
Dengan description.each dan test.each Anda dapat memampatkan kode sebagai berikut:

 const levels = [['manager'], ['supervisor']] const privileges = [['badges', 'toContain', 'badge-'], ['level', 'toBe', '']] describe.each(levels)('When I am a %s', (level) => { test.each(privileges)(`I should have a ${level} %s`, (kind, assert, prefix) => { const employee = new Employee({ level }) expect(employee[kind])[assert](`${prefix}${level}`) }) }) 

Namun, saya belum menggunakan ini dalam tes saya sendiri, karena saya lebih suka tes saya lebih rinci, tetapi saya hanya berpikir itu akan menjadi trik yang menarik.

Lihat dokumentasi untuk detail lebih lanjut tentang argumen (spoiler: sintaks tabel benar-benar keren).

# 5 Imitasi tunggal dari fungsi global


Pada titik tertentu, Anda harus menguji sesuatu yang tergantung pada fungsi global dalam kasus uji tertentu. Misalnya, fungsi yang menerima informasi tentang tanggal saat ini menggunakan objek javascript Date, atau perpustakaan yang bergantung padanya. Kesulitannya adalah ketika sampai pada tanggal saat ini, Anda tidak akan pernah bisa mendapatkan pernyataan yang benar.

 function foo () { return Date.now() } expect(foo()).toBe(Date.now()) // This would throw occasionally: // expect(received).toBe(expected) // Object.is equality // // Expected: 1558881400838 // Received: 1558881400837 


Pada akhirnya, Anda harus mendefinisikan ulang objek Tanggal global agar konsisten dan dapat dikelola:

 function foo () { return Date.now() } Date.now = () => 1234567890123 expect(foo()).toBe(1234567890123) // 

Namun, ini dianggap praktik yang buruk, karena redefinisi dipertahankan antara tes. Anda tidak akan melihat ini jika tidak ada tes lain berdasarkan Date.now, tetapi akan bocor juga.

 test('First test', () => { function foo () { return Date.now() Date.now = () => 1234567890123 expect(foo()).toBe(1234567890123) // }) test('Second test', () => { function foo () { return Date.now() expect(foo()).not.toBe(1234567890123) // ??? }) 

Saya dulu β€œmemecahkannya” agar tidak bocor:

 test('First test', () => { function foo () { return Date.now() const oriDateNow = Date.now Date.now = () => 1234567890123 expect(foo()).toBe(1234567890123) // Date.now = oriDateNow }) test('Second test', () => { function foo () { return Date.now() expect(foo()).not.toBe(1234567890123) // as expected }) 

Namun, ada cara yang jauh lebih baik, lebih sedikit peretas untuk melakukan ini:

 test('First test', () => { function foo () { return Date.now() jest.spyOn(Date, 'now').mockImplementationOnce(() => 1234567890123) expect(foo()).toBe(1234567890123) // }) test('Second test', () => { function foo () { return Date.now() expect(foo()).not.toBe(1234567890123) // as expected }) 

Dengan demikian, jest.spyOn mengikuti objek Date global yang meniru implementasi fungsi now untuk hanya satu panggilan. Ini pada gilirannya akan membuat Date.sekarang utuh untuk sisa tes.

Pasti ada informasi lebih lanjut tentang bertopik di Jest. Lihat dokumentasi lengkap untuk lebih jelasnya.

Artikel ini sudah cukup lama, jadi saya pikir itu saja untuk saat ini. Ini hanya mempengaruhi sebagian kecil dari kemampuan Jest, dan saya hanya menyoroti favorit saya. Jika Anda memiliki fakta menarik lainnya, beri tahu saya.

Juga, jika Anda sering menggunakan Jest, periksa Majestic , yang merupakan GUI bebas GUI untuk Jest, alternatif yang sangat bagus untuk keluaran terminal yang membosankan. Saya tidak yakin apakah penulisnya ada di dev.to, tetapi tetap saja menghormati orang ini.

Seperti biasa, terima kasih atas perhatian Anda!

Itu saja. Sampai jumpa di lapangan.

Source: https://habr.com/ru/post/id457616/


All Articles