Mengapa pengujian unit lebih sulit dalam pemrograman berorientasi objek dibandingkan dengan pemrograman fungsional?

9

Saya akan melalui seri ini . Penulis menyebutkan bahwa karena keadaan dipertahankan dalam pemrograman berorientasi objek, lebih sulit untuk menulis tes unit. Dia juga mengatakan bahwa karena pemrograman fungsional tidak mempertahankan keadaan (tidak selalu), lebih mudah untuk menulis tes unit. Saya tidak melihat contoh yang menunjukkan masalah ini. Jika ini benar, dapatkah Anda memberi saya contoh membandingkan pengujian unit dalam pemrograman berorientasi objek dan fungsional?

0112
sumber
2
Saya tidak setuju dengan gagasan bahwa pemrograman fungsional lebih mudah untuk diuji. Meskipun memang benar fungsi stateless lebih mudah untuk diuji daripada objek stateful, mengubah objek stateful menjadi fungsi stateless menghasilkan fungsi stateles yang jauh lebih kompleks, mungkin dengan beberapa emulasi state kompleks, seperti monad.
Euforia
1
@ Euphoric Suatu terjemahan fungsional dari solusi untuk suatu masalah cenderung sangat berbeda dari sekedar mengganti state dengan Statemonad (yang mengemulasikan state). Anda memiliki sebagian titik - kode yang ditulis dengan gaya itu memang memiliki interaksi non-lokal - tetapi untuk keperluan pengujian unit Anda masih bisa secara eksplisit lulus dalam keadaan, Anda tidak perlu mengejek apa pun. Di sisi lain, dengan monad suka STdan IO(yang memiliki benar, negara bisa berubah implisit), Anda hanya sedang melakukan pemrograman stateful, dan unit pengujian itu akan ada lebih mudah daripada dalam bahasa lain.
Derek Elkins meninggalkan SE
10
Tidak ada dalam definisi OO yang membutuhkan keadaan bisa berubah. Sangat mungkin (dan banyak dilakukan) untuk menulis kode OO yang murni dan transparan.
Jörg W Mittag
1
Semua teknik yang terkait dengan pemrograman fungsional dapat diterapkan dengan sempurna dalam pemrograman berorientasi objek. Program-program OO yang dirancang dengan baik biasanya menggunakan teknik-teknik fungsional di tingkat bawah, minimal.
Frank Hileman
1
@Fabio Jika itu masalahnya, lebih masuk akal untuk menggunakan pemrograman FP. OO tidak memerlukan status dapat diubah, tetapi kemampuan berubah adalah implisit dalam contoh yang saya berikan kepada Anda. Hal yang sama berlaku untuk f # - Anda dapat menandai variabel yang dapat diubah agar sesuai dengan kebutuhan. Tetapi jika Anda akan menggunakan terlalu banyak variabel yang dapat diubah, Anda sebaiknya menggunakan c #.

Jawaban:

17

Saya ingin membedakan antara dua cara yang berbeda dalam mendekati pemrograman berorientasi objek

  1. Simulasi: objek Anda mewakili objek domain nyata, Anda telah memprogramnya untuk menangani fungsionalitas apa pun yang terkait dengan domain itu. Objek yang diprogram dengan cara ini cenderung memiliki banyak keadaan yang bisa berubah dan kolaborator tersembunyi yang digunakan untuk mengimplementasikan fungsi ini.
  2. Fungsi Records +: objek Anda hanyalah kumpulan data dan fungsi yang beroperasi di atas data itu. Objek yang diprogram dengan cara ini lebih cenderung tidak berubah, untuk mengambil lebih sedikit tanggung jawab dan memungkinkan seseorang untuk menyuntikkan kolaborator.

Aturan praktisnya adalah bahwa objek yang diprogram dengan cara pertama akan memiliki lebih banyak metode dan lebih banyak voidmetode daripada cara kedua. Katakanlah kita akan menulis simulator penerbangan dan merancang kelas pesawat. Kami akan memiliki sesuatu seperti:

class Plane {
    void accelerate();
    void deccelerate();
    void toggleRightFlaps();
    void toggleLeftFlaps();
    void turnRudderRight();
    void turnRudderLeft();
    void deployLandingGear();
    void liftLandingGear();
    // etc.
    void tick() throws PlaneCrashedException;
}

Ini mungkin sedikit lebih ekstrem daripada yang mungkin ditemui, tetapi hal ini jelas. Jika Anda ingin mengimplementasikan antarmuka semacam ini, Anda harus tetap di dalam objek:

  1. Semua informasi tentang keadaan peralatan pesawat.
  2. Semua informasi tentang kecepatan / akselerasi pesawat.
  3. Kecepatan refresh dari simulasi kami (untuk menerapkan centang).
  4. Rincian lengkap tentang model 3d simulasi dan fisika untuk menerapkan centang.

Menulis unit test untuk objek yang ditulis dalam mode ini sangat sulit karena:

  1. Anda harus menyediakan semua bit data yang berbeda dan kolaborator yang dibutuhkan objek ini pada awal pengujian Anda (membuat contoh ini bisa sangat membosankan).
  2. Ketika Anda ingin menguji suatu metode, Anda mengalami dua masalah: a) antarmuka seringkali tidak mengekspos data yang cukup untuk Anda uji (sehingga Anda akhirnya harus menggunakan mengejek / refleksi untuk memverifikasi harapan) b) ada banyak komponen yang terikat menjadi satu yang harus Anda verifikasi berperilaku dalam setiap tes.

Pada dasarnya Anda mulai dengan antarmuka yang terlihat masuk akal dan seperti itu cocok dengan domain dengan baik, tetapi kebaikan simulasi telah membuat Anda tertarik untuk membuat objek yang benar-benar sulit untuk diuji.

Namun, Anda dapat membuat objek yang akan memenuhi tujuan yang sama. Anda ingin rem Planemenjadi bit yang lebih kecil. Memiliki PlaneParticleyang melacak bit fisik pesawat, posisi, kecepatan, akselerasi, roll, yaw, dll, dll, mengungkap dan memungkinkan seseorang untuk memanipulasi ini. Kemudian suatu PlanePartsobjek dapat melacak status. Anda akan mengirim tick()ke tempat yang sama sekali berbeda, misalnya memiliki PlanePhysicsparameter objek oleh, misalnya, gaya gravitasi, yang tahu diberi PlaneParticledan PlanePartsbagaimana meludahkan yang baru PlaneParticle. Semua ini bisa sepenuhnya berubah, meskipun tidak harus untuk beberapa contoh.

Anda sekarang memiliki keunggulan ini dalam hal pengujian:

  1. Setiap komponen individu memiliki lebih sedikit pekerjaan dan lebih mudah diatur.
  2. Anda dapat menguji komponen Anda secara terpisah.
  3. Objek-objek ini dapat lolos dengan mengekspos internal mereka (terutama jika mereka dibuat abadi), sehingga orang tidak perlu kepintaran untuk mengukurnya.

Inilah triknya: pendekatan berorientasi objek kedua yang saya jelaskan sangat dekat dengan pemrograman fungsional. Mungkin dalam program fungsional murni catatan Anda dan fungsi Anda terpisah dan tidak terikat bersama dalam objek, pasti program fungsional akan memastikan bahwa semua hal ini. Apa yang saya pikir benar-benar membuat unit testing mudah adalah

  1. Unit kecil (ruang keadaan kecil).
  2. Fungsinya dengan input minimal (tidak ada input tersembunyi).
  3. Fungsinya dengan output minimal.

Pemrograman fungsional mendorong hal-hal ini (tetapi seseorang dapat menulis program yang buruk dalam paradigma apa pun) tetapi mereka dapat dicapai dalam program berorientasi objek. Dan saya akan lebih menekankan bahwa pemrograman fungsional dan pemrograman berorientasi objek tidak kompatibel.

walpen
sumber
2
Saya tidak yakin ini perbedaan yang bagus. Bahkan ketika menggunakan pendekatan 1, banyak yang akan mempertimbangkan teknik yang Anda gambarkan untuk pendekatan 2 sebagai praktik standar. Kedua pendekatan tersebut tidak saling eksklusif. Potongan-potongan Pesawat dapat dikomposisikan bersama untuk membuat abstraksi yang lebih besar, dan Anda sekarang berada di area pendekatan 1. Saya akan sejauh mengatakan pendekatan 1, seperti yang Anda jelaskan, tidak logis.
Frank Hileman
3
"Dan saya akan lebih menekankan bahwa pemrograman fungsional dan pemrograman berorientasi objek tidak kompatibel.": Beberapa orang akan mengatakan bahwa pemrograman berorientasi objek hanya ditandai oleh pengiriman dinamis. Jadi, Anda dapat menulis dalam gaya OOP imperatif : objek yang dapat diubah + pengiriman dinamis, atau dalam gaya OOP fungsional : objek tidak berubah + pengiriman dinamis.
Giorgio
3
Memecah 1 objek besar menjadi bagian yang lebih kecil bukanlah pemrograman fungsional. Dan solusi untuk objek besar adalah tidak memisahkan logika dari data dan membuatnya menjadi anemia, yang fungsional . Saya mungkin salah paham seluruh bagian Anda namun .
Chris Wohlert