Bagaimana cara mendesain perangkat lunak game sehingga mudah untuk unit test?

25

Apakah praktis menggunakan kerangka pengujian seperti JUnit dalam situasi pengembangan game? Pertimbangan desain seperti apa yang bisa Anda ikuti untuk membuat game Anda lebih teruji? Bagian mana dari sebuah game yang dapat / harus diuji dan bagian mana yang harus / harus diserahkan kepada pengujian manusia?

Misalnya, jika loop game diringkas dalam satu fungsi, sepertinya akan sangat sulit untuk menguji. Saya suka menolak fungsi "pembaruan" yang membutuhkan waktu dan mengubah logika permainan; ini memungkinkan beberapa trik menarik seperti kemampuan untuk memperlambat gim dengan memberi makan delta palsu, waktu yang lebih lambat.

Ricket
sumber
6
Mengapa buang-buang waktu menulis unit test ketika Anda memiliki pasukan budak yang tak ada habisnya untuk melakukan playtesting bagi Anda? I kid, I kid ...;)
Mike Strobel
1
Sebenarnya kamu tidak perlu nak, itu poin yang sangat bagus! Saya tidak memikirkan hal itu, tetapi permainan adalah jenis perangkat lunak yang paling mudah untuk diuji orang lain. Saya kira itu agak mengimbangi kesulitan pengujian game otomatis (unit atau sebaliknya).
Ricket
3
Pengujian unit tidak selalu tentang memastikan aplikasi Anda berfungsi dengan baik secara keseluruhan (pengujian fungsional). Ini lebih lanjut tentang memastikan perubahan di masa depan tidak merusak fungsi yang ada. Tentu, pengguna dapat melihat kapan pesawat ruang angkasa terbalik, tetapi ketika algoritma pathing dimatikan oleh 0,001, efeknya mungkin tidak terlihat sampai permainan dimainkan selama 200 jam. Itulah jenis hal yang dapat ditangkap unit sebelum kode keluar.
Wade Williams
1
Game adalah perangkat lunak termudah untuk membuat pengguna bermain , tetapi bermain! = Menguji . Mendapatkan laporan bug yang bagus cukup sulit. Selain itu, melacak di mana dalam kode bug terjadi, dan memverifikasi bahwa perubahan baru tidak merusak kode yang ada, keduanya mendapat manfaat secara dramatis dari pengujian otomatis.
murah hati

Jawaban:

25

Salah satu prinsip TDD adalah Anda membiarkan TDD dalam beberapa kasus memengaruhi desain Anda. Anda menulis tes untuk sistem, kemudian menulis kode untuk membuat lulus tes itu, menjaga dependensi sedangkal mungkin.

Bagi saya, hanya ada dua hal yang tidak saya uji sebagai bagian dari pengujian unit:

Pertama, saya tidak menguji elemen visual dan bagaimana tampilannya. Saya mengujinya dan objek akan berada di tempat yang tepat setelah pembaruan, bahwa kamera akan menyisihkan objek di luar batasnya, bahwa transformasi (setidaknya yang dilakukan di luar shaders) dilakukan dengan benar sebelum diserahkan ke mesin grafis , tetapi begitu menyentuh sistem grafis saya menarik garis. Saya tidak suka mencoba mengejek hal-hal seperti DirectX.

Kedua, saya tidak benar-benar menguji fungsi loop game utama. Saya menguji bahwa setiap sistem akan bekerja ketika melewati delta yang masuk akal, dan bahwa sistem bekerja bersama dengan benar ketika mereka perlu. Lalu saya baru saja memperbarui setiap sistem dengan delta yang benar di loop game. Saya benar-benar dapat memiliki tes untuk menunjukkan bahwa setiap sistem dipanggil dengan delta yang benar, tetapi dalam banyak kasus saya menemukan bahwa pembunuhan berlebihan (kecuali jika Anda melakukan logika kompleks untuk mendapatkan delta Anda, maka itu tidak berlebihan).

Jeff
sumber
6
Paragraf pertama adalah kunci. Faktanya adalah bahwa TDD mengharuskan Anda untuk merancang kode Anda lebih baik daripada yang Anda miliki tanpa itu, hanya untuk memungkinkan pengujian. Tentu saja banyak yang berpikir bahwa mereka ahli dalam mendesain, tetapi tentu saja mereka yang paling terkesan dengan keterampilan mereka sendiri biasanya adalah yang paling banyak belajar ...
dash-tom-bang
2
Saya tidak akan setuju bahwa kode tersebut dirancang dengan lebih baik sebagai hasilnya. Saya telah melihat banyak kekejian di mana antarmuka yang sebelumnya bersih harus dihancurkan terbuka dan enkapsulasi rusak untuk menyuntikkan dependensi yang dapat diuji.
Kylotan
4
Saya menemukan itu terjadi lebih banyak dalam hal di mana tes ditulis untuk kelas yang sudah ada daripada sebaliknya.
Jeff
@ Kylotan itu mungkin lebih merupakan gejala desain yang tidak tepat / desain tes yang buruk. Refactor dan gunakan tiruan untuk melakukan tes bersih, jangan hack kode Anda ke keadaan yang lebih buruk; itu kebalikan dari apa yang seharusnya terjadi.
timoxley
2
Enkapsulasi adalah hal yang baik, tetapi yang lebih penting adalah untuk memahami MENGAPA itu adalah hal yang baik. Jika Anda hanya menggunakannya untuk menyembunyikan dependensi dan antarmuka yang jelek itu cukup banyak berteriak bahwa desain Anda di bawah tenda mungkin tidak terlalu bagus. Enkapsulasi terutama bukan tentang menyembunyikan kode jelek dan dependensi, ini terutama tentang membuat kode Anda lebih mudah dipahami dan untuk membiarkan konsumen mendapatkan lebih sedikit jalur untuk diikuti ketika ia mencoba untuk berpikir tentang kode.
Martin Odhelius
19

Noel Llopis telah membahas pengujian unit sampai tingkat yang saya yakin Anda cari:

Sehubungan dengan pengujian seluruh loop game, ada teknik lain untuk mencegah regresi fungsional dalam kode Anda. Idenya adalah membuat gim Anda bermain sendiri melalui sistem replay (mis. Pertama-tama merekam input pemain, dan kemudian memutar ulangnya dalam jangka yang berbeda). Selama pemutaran ulang, Anda menghasilkan potret keadaan setiap objek untuk setiap bingkai. Koleksi negara ini kemudian dapat disebut "gambar emas". Saat refactoring kode Anda, Anda menjalankan replay lagi dan membandingkan masing-masing keadaan frame dengan keadaan gambar emas. Jika berbeda, tes gagal.

Meskipun manfaat dari teknik ini jelas, ada beberapa hal yang perlu Anda perhatikan:

  • Gim Anda harus deterministik, jadi Anda harus berhati-hati dengan hal-hal seperti bergantung pada jam sistem, peristiwa sistem / jaringan, generator angka acak yang tidak diunggulkan secara deterministik. dll.
  • perubahan konten setelah gambar emas dihasilkan dapat menyebabkan perbedaan yang sah dengan gambar emas Anda. Tidak ada cara untuk mengatasi ini - saat mengubah konten Anda, Anda perlu membuat ulang gambar emas Anda.
jpaver
sumber
+1 Juga patut dicatat bahwa nilai pendekatan ini terkait langsung dengan panjang "gambar emas": Jika tidak cukup panjang, Anda dapat dengan mudah kehilangan bug; jika terlalu lama, Anda akan memiliki banyak menunggu untuk dilakukan. Sangat penting untuk mencoba dan membuat gim ini berjalan mungkin dengan urutan besarnya lebih cepat dalam mode ini, daripada yang Anda lakukan dalam keadaan runtime normal, untuk mengurangi waktu istirahat. Melompati rendering dapat membantu!
Insinyur
9

Saya setuju dengan komentar Jeff dan jpaver. Saya juga ingin menambahkan bahwa mengadopsi model komponen untuk arsitektur Anda sangat meningkatkan testabilitasnya. Dengan model komponen, setiap komponen harus melakukan satu unit kerja dan harus dapat diuji secara terpisah (atau dengan benda tiruan terbatas).

Demikian juga, bagian lain dari gim yang mengandalkan entitas seharusnya hanya mengandalkan subset komponen yang berfungsi. Dalam praktiknya ini berarti Anda biasanya dapat mengejek beberapa komponen palsu untuk memfasilitasi pengujian area ini atau Anda dapat dengan mudah membuat entitas parsial untuk tujuan pengujian. misalnya Anda meninggalkan komponen rendering dan input karena komponen tersebut tidak diperlukan untuk menguji fisika.

Akhirnya, saya akan mengabaikan saran seputar kinerja yang Anda dapatkan dari beberapa orang di komunitas game tentang antarmuka. Tulis kode dengan baik dengan antarmuka dan jika Anda mengalami masalah kinerja di jalan Anda dapat dengan mudah profil, mengidentifikasi, dan refactor untuk menyelesaikan masalah. Saya tidak dibujuk oleh kekhawatiran Noel tentang dampak kinerja antarmuka atau overhead kompleksitas yang mereka tambahkan.

Yang mengatakan, jangan berlebihan mencoba untuk menguji setiap hal kecil secara mandiri. Seperti kebanyakan hal pengujian dan desain adalah tentang keseimbangan yang tepat.

Alex Schearer
sumber
Harap dicatat, meskipun Anda tampaknya bukan orang baru di SX, jawaban tidak diurutkan dan mengatakan sesuatu seperti "kedua komentar di atas" pasti akan segera usang, karena saat ini Anda memilih satu suara di depan satu suara dari dua jawaban lain saat ini. :)
Ricket
Terima kasih saya tidak memikirkan hal itu ketika berkomentar. Saya telah memperbarui komentar untuk memilih komentar individu.
Alex Schearer
3

Saya pikir saya akan menambahkan jawaban kedua menanggapi komentar OP bahwa pengguna dapat mengganti tes unit. Saya percaya itu sepenuhnya salah karena tujuan unit test bukan untuk menjamin kualitas. Jika Anda ingin tes tersedia untuk memastikan kualitas program Anda, Anda harus menyelidiki tes skenario atau berinvestasi dalam pemantauan yang baik.

(Dengan pemantauan dan produk yang berjalan sebagai layanan, memang mungkin untuk mendorong "pengujian" ke pelanggan tetapi Anda harus memiliki sistem canggih yang mendeteksi kesalahan dengan cepat dan mengembalikan perubahan yang bertanggung jawab. Kemungkinan besar ini terlalu banyak untuk tim pengembangan kecil.)

Manfaat utama pengujian unit adalah bahwa mereka memaksa pengembang untuk menulis kode yang dirancang lebih baik. Tindakan pengujian menantang pengembang untuk memisahkan masalah dan merangkum data. Ini juga mendorong pengembang untuk menggunakan antarmuka dan abstraksi lain sehingga ia hanya menguji satu hal.

Alex Schearer
sumber
Setuju, kecuali bahwa unit test tidak memaksa pengembang untuk menulis kode yang baik. Ada pilihan lain: tes yang ditulis sangat buruk. Anda perlu komitmen pada gagasan unit test dari coders Anda untuk menghentikannya.
tenpn
2
Tentu tapi itu bukan alasan untuk membuang bayi keluar dengan air mandi. Gunakan alat secara bertanggung jawab tetapi pertama-tama gunakan alat tersebut.
Alex Schearer