Memecahkan masalah yang menyertai fungsi dyadic assertEquals (diharapkan, aktual)

10

Setelah bertahun-tahun pengkodean koboi, saya memutuskan untuk mengambil buku tentang cara menulis kode berkualitas baik. Saya membaca Kode Bersih oleh Robert Cecil Martin. Dalam bab 3 (fungsi) ada bagian tentang fungsi diad. Ini adalah kutipan dari buku ini.

Bahkan fungsi diad yang jelas seperti assertEquals(expected, actual)bermasalah. Berapa kali Anda meletakkan aktual di mana yang diharapkan seharusnya? Kedua argumen tidak memiliki urutan alami. Pemesanan yang diharapkan dan aktual adalah konvensi yang membutuhkan latihan untuk belajar.

Penulis membuat poin yang meyakinkan. Saya bekerja dalam pembelajaran mesin dan menemukan ini sepanjang waktu. Misalnya, semua fungsi metrik di pustaka sklearn (mungkin pustaka python paling banyak digunakan di bidang) mengharuskan Anda untuk berhati-hati dengan urutan input. Sebagai contoh, sklearn.metrics.homogeneity_score sebagai input labels_truedan labels_pred. Apa fungsi ini tidak terlalu relevan, apa yang relevan adalah bahwa jika Anda mengganti urutan input tidak ada kesalahan akan dilemparkan. Sebenarnya beralih input sama dengan menggunakan fungsi lain di perpustakaan.

Namun buku ini tidak melanjutkan untuk mengatakan perbaikan yang masuk akal untuk fungsi seperti assertEquals. Saya tidak bisa memikirkan perbaikan untuk assertEqualsatau untuk fungsi yang sering saya temui seperti yang dijelaskan di atas. Apa praktik yang baik untuk mengatasi masalah ini?

HBeel
sumber

Jawaban:

11

Adalah baik untuk mengetahui kemungkinan masalah bahkan ketika tidak ada perbaikan - dengan begitu Anda bisa waspada saat membaca atau menulis kode seperti itu. Dalam contoh khusus ini, Anda baru terbiasa dengan urutan argumen setelah beberapa saat.

Ada cara tingkat bahasa untuk mencegah kebingungan tentang urutan parameter: argumen bernama. Sayangnya ini tidak didukung dalam banyak bahasa dengan sintaks gaya C seperti Java atau C ++. Namun dengan Python, setiap argumen bisa berupa argumen bernama. Alih-alih memanggil fungsi def foo(a, b)sebagai foo(1, 2), kita bisa melakukannya foo(a=1, b=2). Banyak bahasa modern seperti C # memiliki sintaksis yang serupa. Keluarga bahasa Smalltalk telah mengambil argumen bernama terjauh: tidak ada argumen posisi dan semuanya dinamai. Ini dapat menyebabkan kode yang berbunyi sangat dekat dengan bahasa alami.

Alternatif yang lebih praktis adalah membuat API yang mensimulasikan argumen bernama. Ini bisa berupa API yang lancar, atau fungsi pembantu yang membuat aliran alami. The assertEquals(actual, expected)Nama membingungkan. Beberapa alternatif yang saya lihat:

  • assertThat(actual, is(equalTo(expected))): dengan membungkus beberapa argumen dalam tipe pembantu, fungsi pembungkus secara efektif berfungsi sebagai nama parameter. Dalam kasus spesifik asersi unit test, teknik ini digunakan oleh pencocokan Hamcrest untuk menyediakan sistem asersi yang dapat dikembangkan dan dikomposisikan. Kerugiannya di sini adalah Anda mendapatkan banyak sarang, dan perlu mengimpor banyak fungsi pembantu. Ini adalah teknik masuk saya ke dalam C ++.

  • expect(actual).to.be(expected): API yang lancar tempat Anda membuat string berfungsi bersamaan. Meskipun ini menghindari bersarang ekstra, ini tidak terlalu dapat diperpanjang. Meskipun saya mendapati bahwa API lancar membaca dengan sangat baik, merancang API lancar yang baik cenderung membutuhkan banyak upaya dalam pengalaman saya, karena Anda perlu menerapkan kelas tambahan untuk status non-terminal dalam rantai panggilan. Upaya ini hanya benar-benar terbayar dalam konteks IDE pelengkapan otomatis yang dapat menyarankan pemanggilan metode selanjutnya yang diizinkan.

amon
sumber
4

Ada beberapa metode untuk menghindari masalah ini. Yang tidak memaksa Anda untuk mengubah metode yang Anda panggil:

Daripada

assertEquals( 42, meaningOfLife() ); 

Menggunakan

expected = 42;
actual = meaningOfLife();
assertEquals(expected, actual);

Ini memaksa konvensi keluar ke tempat terbuka di mana mudah dikenali mereka beralih. Tentu tidak mudah untuk menulis tetapi mudah dibaca.

Jika Anda dapat mengubah metode yang dipanggil, Anda dapat menggunakan sistem pengetikan untuk memaksa penggunaan yang mudah dibaca.

assertThat( meaningOfLife(), is(42) );

Beberapa bahasa membiarkan Anda menghindari ini karena mereka telah menamai parameter:

assertEquals( expected=42, actual=meaningOfLife() );

Orang lain tidak jadi Anda mensimulasikan mereka:

assertEquals().expected(42).actual( meaningOfLife() );

Apa pun yang Anda temukan cara yang membuatnya jelas mana yang benar ketika dibaca. Jangan buat saya menebak apa konvensi itu. Tunjukkan itu padaku.

candied_orange
sumber