Bagaimana seharusnya Anda memainkan game Yahtzee?

36

Katakanlah Anda sedang menulis gaya TDD game Yahtzee. Anda ingin menguji bagian dari kode yang menentukan apakah satu set lima gulungan mati adalah rumah penuh. Sejauh yang saya tahu, ketika melakukan TDD, Anda mengikuti prinsip-prinsip ini:

  • Tulis tes terlebih dahulu
  • Tulis hal paling sederhana yang berhasil
  • Memperbaiki dan memperbaiki

Jadi tes awal mungkin terlihat seperti ini:

public void Returns_true_when_roll_is_full_house()
{
    FullHouseTester sut = new FullHouseTester();
    var actual = sut.IsFullHouse(1, 1, 1, 2, 2);

    Assert.IsTrue(actual);
}

Saat mengikuti "Tulis hal paling sederhana yang berhasil", Anda sekarang harus menulis IsFullHousemetode seperti ini:

public bool IsFullHouse(int roll1, int roll2, int roll3, int roll4, int roll5)
{
    if (roll1 == 1 && roll2 == 1 && roll3 == 1 && roll4 == 2 && roll5 == 2)
    {
        return true;
    }

    return false;
}

Ini menghasilkan tes hijau tetapi implementasinya tidak lengkap.

Haruskah Anda menguji setiap kemungkinan kombinasi yang valid (baik nilai dan posisi) untuk rumah lengkap? Itu sepertinya satu-satunya cara untuk benar-benar yakin bahwa IsFullHousekode Anda benar-benar diuji dan benar, tetapi juga terdengar sangat gila untuk melakukan itu.

Bagaimana Anda menguji sesuatu seperti ini?

Memperbarui

Erik dan Kilian menunjukkan bahwa menggunakan literal dalam implementasi awal untuk mendapatkan tes hijau mungkin bukan ide terbaik. Saya ingin menjelaskan mengapa saya melakukan itu dan penjelasan itu tidak sesuai dengan komentar.

Pengalaman praktis saya dengan pengujian unit (terutama menggunakan pendekatan TDD) sangat terbatas. Saya ingat menonton rekaman Masterclass TDD Roy Osherove di Tekpub. Dalam salah satu episode ia membangun gaya Kalkulator String TDD. Spesifikasi lengkap dari String Calculator dapat ditemukan di sini: http://osherove.com/tdd-kata-1/

Dia mulai dengan tes seperti ini:

public void Add_with_empty_string_should_return_zero()
{
    StringCalculator sut = new StringCalculator();
    int result = sut.Add("");

    Assert.AreEqual(0, result);
}

Ini menghasilkan implementasi Addmetode ini yang pertama :

public int Add(string input)
{
    return 0;
}

Kemudian tes ini ditambahkan:

public void Add_with_one_number_string_should_return_number()
{
    StringCalculator sut = new StringCalculator();
    int result = sut.Add("1");

    Assert.AreEqual(1, result);
}

Dan Addmetodenya adalah refactored:

public int Add(string input)
{
    if (input.Length == 0)
    {
        return 0;
    }

    return 1;
}

Setelah setiap langkah Roy mengatakan "Tulis hal paling sederhana yang akan berhasil".

Jadi saya pikir saya akan mencoba pendekatan ini ketika mencoba untuk melakukan permainan Yahtzee gaya TDD.

Kristof Claes
sumber
8
"Tulis hal yang paling sederhana yang berhasil" adalah singkatan; saran yang benar adalah "Tulis hal paling sederhana yang mungkin tidak sepenuhnya braindead dan jelas salah yang berhasil". Jadi, tidak, Anda tidak boleh menulisif (roll1 == 1 && roll2 == 1 && roll3 == 1 && roll4 == 2 && roll5 == 2)
Carson63000
3
Terima kasih telah merangkum jawaban Erik, baik dengan cara yang kurang argumentatif atau beradab.
Kristof Claes
1
"Tulis hal paling sederhana yang berhasil", seperti @ Carson63000, sebenarnya adalah penyederhanaan. Sebenarnya berbahaya berpikir seperti itu; itu mengarah ke bencana TDD Sudoku yang terkenal (google it). Ketika diikuti secara membabi buta, TDD memang braindead: Anda tidak dapat menggeneralisasi algoritma non-sepele dengan secara membabi buta melakukan "hal paling sederhana yang berhasil" ... Anda harus benar-benar berpikir! Sayangnya, bahkan master yang diduga dari XP dan TDD kadang-kadang mengikutinya secara membabi buta ...
Andres F.
1
@AndresF. Perhatikan bahwa komentar Anda muncul lebih tinggi di pencarian Google daripada banyak komentar tentang "bencana Soduko TDD" setelah kurang dari tiga hari. Namun demikian Bagaimana tidak memecahkan Sudoku menyimpulkannya: TDD adalah untuk kualitas, bukan kebenaran. Anda perlu menyelesaikan algoritme sebelum memulai pengkodean, terutama dengan TDD. (Bukannya saya bukan programmer kode pertama juga.)
Mark Hurd

Jawaban:

40

Sudah ada banyak jawaban bagus untuk pertanyaan ini, dan saya sudah mengomentari dan membatalkan beberapa di antaranya. Tetap saja, saya ingin menambahkan beberapa pemikiran.

Fleksibilitas bukan untuk pemula

OP dengan jelas menyatakan bahwa dia tidak berpengalaman dengan TDD, dan saya pikir jawaban yang baik harus memperhitungkannya. Dalam terminologi model Dreyfus tentang perolehan keterampilan , dia mungkin seorang pemula . Tidak ada yang salah dengan menjadi novis - kita semua adalah novis ketika kita mulai mempelajari sesuatu yang baru. Namun, apa yang dijelaskan oleh model Dreyfus adalah bahwa para pemula ditandai oleh

  • kepatuhan ketat terhadap aturan atau rencana yang diajarkan
  • tidak menggunakan penilaian diskresi

Itu bukan deskripsi tentang kekurangan kepribadian, jadi tidak ada alasan untuk malu akan hal itu - ini adalah tahap yang harus kita semua lalui untuk mempelajari sesuatu yang baru.

Ini juga berlaku untuk TDD.

Sementara saya setuju dengan banyak jawaban lain di sini bahwa TDD tidak harus dogmatis, dan bahwa kadang-kadang bisa lebih bermanfaat untuk bekerja dengan cara alternatif, itu tidak membantu siapa pun yang baru memulai. Bagaimana Anda bisa melakukan penilaian bebas ketika Anda tidak memiliki pengalaman?

Jika seorang pemula menerima saran bahwa kadang-kadang tidak apa-apa untuk tidak melakukan TDD, bagaimana ia bisa menentukan kapan tidak apa-apa untuk tidak melakukan TDD?

Tanpa pengalaman atau bimbingan, satu-satunya hal yang dapat dilakukan seorang pemula adalah keluar dari TDD setiap kali menjadi terlalu sulit. Itu sifat manusia, tetapi bukan cara yang baik untuk belajar.

Dengarkan tesnya

Melewatkan TDD setiap saat menjadi sulit adalah kehilangan salah satu manfaat terpenting dari TDD. Pengujian memberikan umpan balik awal tentang API SUT. Jika tes ini sulit untuk ditulis, itu pertanda penting bahwa SUT sulit digunakan.

Inilah alasan mengapa salah satu pesan paling penting dari GOOS adalah: dengarkan tes Anda!

Dalam kasus pertanyaan ini, reaksi pertama saya ketika melihat API yang diusulkan dari game Yahtzee, dan diskusi tentang kombinatorik yang dapat ditemukan di halaman ini, adalah bahwa ini adalah umpan balik penting tentang API.

Apakah API harus mewakili gulungan dadu sebagai urutan bilangan bulat yang dipesan? Bagi saya, itu bau Obsesi Primitif . Itu sebabnya saya senang melihat jawaban dari tallseth yang menyarankan pengenalan Rollkelas. Saya pikir itu saran yang bagus.

Namun, saya berpikir bahwa beberapa komentar untuk jawaban itu salah. Apa yang kemudian disarankan oleh TDD adalah bahwa begitu Anda mendapatkan ide bahwa suatu Rollkelas akan menjadi ide yang baik, Anda menangguhkan pekerjaan pada SUT asli dan mulai mengerjakan TDD di Rollkelas.

Meskipun saya setuju bahwa TDD lebih ditujukan pada 'happy path' daripada pengujian komprehensif, namun tetap membantu memecah sistem menjadi beberapa unit yang dapat dikelola. Sebuah Rollsuara kelas seperti sesuatu yang Anda bisa TDD selesai jauh lebih mudah.

Kemudian, begitu Rollkelas telah cukup berkembang, apakah Anda akan kembali ke SUT asli dan menyempurnakannya dalam hal Rollinput.

Saran dari Test Helper tidak selalu menyiratkan keacakan - itu hanya cara untuk membuat tes lebih mudah dibaca.

Cara lain untuk mendekati dan memodelkan input dalam hal Rollinstance adalah dengan memperkenalkan Test Data Builder .

Merah / Hijau / Refactor adalah proses tiga tahap

Sementara saya setuju dengan sentimen umum bahwa (jika Anda cukup berpengalaman dalam TDD), Anda tidak perlu menggunakan TDD dengan ketat, saya pikir itu saran yang sangat buruk dalam kasus latihan Yahtzee. Meskipun saya tidak tahu detail dari peraturan Yahtzee, saya tidak melihat argumen yang meyakinkan di sini bahwa Anda tidak dapat bertahan dengan ketat dengan proses Merah / Hijau / Refactor dan masih sampai pada hasil yang tepat.

Apa yang kebanyakan orang di sini sepertinya lupa adalah tahap ketiga dari proses Merah / Hijau / Refactor. Pertama, Anda menulis tes. Kemudian Anda menulis implementasi paling sederhana yang melewati semua tes. Maka Anda refactor.

Di sini, di negara bagian ketiga ini, Anda dapat membawa semua keterampilan profesional Anda. Di sinilah Anda diizinkan untuk merenungkan kode.

Namun, saya pikir ini adalah alasan untuk menyatakan bahwa Anda seharusnya hanya "Menulis hal yang paling sederhana yang tidak sepenuhnya braindead dan jelas salah yang berhasil". Jika Anda (berpikir Anda) cukup tahu tentang implementasi sebelumnya, maka semua kekurangan solusi lengkap akan menjadi jelas salah . Sejauh saran berjalan, maka, ini cukup berguna bagi seorang pemula.

Apa yang benar-benar harus terjadi adalah bahwa jika Anda dapat membuat semua tes lulus dengan implementasi yang jelas salah , itu umpan balik yang harus Anda tulis tes lain .

Mengejutkan betapa seringnya melakukan itu menuntun Anda ke arah implementasi yang sama sekali berbeda dari yang Anda pikirkan pertama kali. Terkadang, alternatif yang tumbuh seperti itu ternyata lebih baik dari rencana semula.

Kekakuan adalah alat belajar

Sangat masuk akal untuk tetap dengan proses yang ketat seperti Merah / Hijau / Refactor selama kita masih belajar. Ini memaksa pelajar untuk mendapatkan pengalaman dengan TDD tidak hanya saat itu mudah, tetapi juga saat itu sulit.

Hanya ketika Anda telah menguasai semua bagian yang sulit Anda berada dalam posisi untuk membuat keputusan berdasarkan informasi tentang kapan harus menyimpang dari jalan 'benar'. Saat itulah Anda mulai membentuk jalur Anda sendiri.

Mark Seemann
sumber
'nother TDD pemula di sini, dengan semua keraguan tentang mencobanya. Mengambil yang menarik jika Anda dapat membuat semua tes lulus dengan implementasi yang jelas salah, itu umpan balik bahwa Anda harus menulis tes lain. Sepertinya cara yang baik untuk mengatasi persepsi bahwa menguji implementasi "braindead" adalah pekerjaan yang tidak perlu.
Shambulator
1
Wow Terimakasih. Saya benar-benar takut dengan kecenderungan orang untuk memberitahu pemula di TDD (atau disiplin apa pun) untuk "jangan khawatir tentang aturan, lakukan saja apa yang terasa terbaik". Bagaimana Anda bisa tahu apa yang terasa terbaik ketika Anda tidak memiliki pengetahuan atau pengalaman? Saya juga ingin menyebutkan prinsip prioritas transformasi, atau kode itu harus menjadi lebih umum ketika tes menjadi lebih spesifik. pendukung TDD paling keras seperti paman bob tidak akan berdiri di belakang gagasan "hanya menambahkan pernyataan jika-baru untuk setiap tes".
sara
41

Sebagai penafian, ini adalah TDD ketika saya mempraktikkannya dan, sebagaimana ditunjukkan Kilian dengan tepat, saya akan mewaspadai siapa pun yang menyarankan bahwa ada satu cara yang tepat untuk mempraktikkannya. Tapi mungkin itu akan membantu Anda ...

Pertama-tama, hal paling sederhana yang dapat Anda lakukan untuk lulus ujian adalah:

public bool IsFullHouse(int roll1, int roll2, int roll3, int roll4, int roll5)
{
    return true;
}

Ini penting karena bukan karena beberapa praktik TDD, tetapi karena hard-cording dalam semua literal itu sebenarnya bukan ide yang baik. Salah satu hal paling sulit untuk membungkus kepala Anda dengan TDD adalah bahwa itu bukan strategi pengujian yang komprehensif - itu adalah cara untuk menjaga terhadap regresi dan menandai kemajuan sambil menjaga kode tetap sederhana. Ini adalah strategi pengembangan dan bukan strategi pengujian.

Alasan saya menyebutkan perbedaan ini adalah membantu memandu tes apa yang harus Anda tulis. Jawaban untuk "tes apa yang harus saya tulis?" adalah "tes apa pun yang Anda butuhkan untuk mendapatkan kode seperti yang Anda inginkan." Pikirkan TDD sebagai cara untuk membantu Anda mencari algoritma dan alasan tentang kode Anda. Jadi, mengingat tes Anda dan penerapan "hijau sederhana" saya, tes apa yang akan terjadi selanjutnya? Nah, Anda telah menetapkan sesuatu yang merupakan rumah penuh, jadi kapan bukan rumah penuh?

public void Returns_true_when_roll_is_full_house()
{
    FullHouseTester sut = new FullHouseTester();
    var actual = sut.IsFullHouse(1, 2, 3, 4, 5);

    Assert.IsFalse(actual);
}

Sekarang Anda harus mencari cara untuk membedakan antara dua kasus uji yang bermakna . Saya pribadi akan menempelkan sedikit informasi klarifikasi ke "lakukan hal paling sederhana untuk membuat lulus ujian" dan mengatakan "lakukan hal paling sederhana untuk membuat lulus ujian yang semakin jauh implementasi Anda." Menulis tes yang gagal adalah alasan Anda untuk mengubah kode, jadi ketika Anda menulis setiap tes, Anda harus bertanya pada diri sendiri "apa yang tidak saya lakukan dengan kode yang saya inginkan dan bagaimana saya bisa mengekspos kekurangan itu?" Ini juga dapat membantu Anda membuat kode Anda kuat dan menangani kasus tepi. Apa yang Anda lakukan jika seorang pemanggil memasukkan omong kosong?

public void Returns_true_when_roll_is_full_house()
{
    FullHouseTester sut = new FullHouseTester();
    var actual = sut.IsFullHouse(-1, -2, -3, -4, -5);

    //I dunno - throw exception, return false, etc, whatever you think it should do....
}

Singkatnya, jika Anda menguji setiap kombinasi nilai, Anda hampir pasti salah melakukannya (dan kemungkinan akan berakhir dengan ledakan kombinasi conditional). Ketika datang ke TDD, Anda harus menulis jumlah minimum kasus uji yang diperlukan untuk mendapatkan algoritma yang Anda inginkan. Setiap tes lebih lanjut yang Anda tulis akan mulai hijau dan dengan demikian menjadi dokumentasi, pada dasarnya, dan bukan bagian dari proses TDD. Anda hanya akan menulis kasus uji TDD lebih lanjut jika persyaratan berubah atau bug terbuka, dalam hal ini Anda akan mendokumentasikan kekurangan dengan tes dan kemudian membuatnya lulus.

Memperbarui:

Saya memulai ini sebagai komentar untuk menanggapi pembaruan Anda, tetapi mulai cukup lama ...

Saya akan mengatakan bahwa masalahnya bukan dengan keberadaan literal, titik, tetapi dengan hal 'paling sederhana' menjadi syarat 5-bagian. Ketika Anda memikirkannya, syarat 5-bagian sebenarnya cukup rumit. Adalah umum untuk menggunakan literal selama langkah merah ke hijau dan kemudian mengabstraksikannya menjadi konstanta pada langkah refactor atau menggeneralisasikannya dalam pengujian selanjutnya.

Selama perjalanan saya sendiri dengan TDD, saya menyadari bahwa ada perbedaan penting yang harus dibuat - itu tidak baik untuk membingungkan "sederhana" dan "tumpul". Yaitu, ketika saya mulai, saya melihat orang-orang melakukan TDD dan saya pikir "mereka hanya melakukan hal yang paling bodoh untuk membuat tes lulus" dan saya menirukannya sebentar, sampai saya menyadari bahwa "sederhana" agak berbeda. dari "tumpul". Terkadang mereka tumpang tindih, tetapi seringkali tidak.

Jadi, permintaan maaf jika saya memberi kesan bahwa keberadaan literal adalah masalahnya - tidak. Saya akan mengatakan kompleksitas persyaratan dengan 5 klausa adalah masalahnya. Merah-ke-hijau pertama Anda bisa saja "mengembalikan benar" karena itu benar-benar sederhana (dan tumpul, secara kebetulan). Kasing uji berikutnya, dengan (1, 2, 3, 4, 5) harus mengembalikan false, dan di sinilah Anda mulai meninggalkan "tumpul". Anda harus bertanya pada diri sendiri "mengapa (1, 1, 1, 2, 2) rumah penuh dan (1, 2, 3, 4, 5) tidak?" Hal paling sederhana yang Anda dapat lakukan adalah bahwa seseorang memiliki elemen urutan terakhir 5 atau elemen urutan kedua 2 dan yang lainnya tidak. Itu sederhana, tetapi mereka juga tumpul (tidak perlu). Apa yang Anda benar-benar ingin kendarai adalah "berapa jumlah yang sama yang mereka miliki?" Jadi, Anda mungkin mendapatkan tes kedua untuk lulus dengan memeriksa untuk melihat apakah ada pengulangan atau tidak. Dalam satu dengan pengulangan, Anda memiliki rumah penuh, dan yang lain tidak. Sekarang tes berlalu, dan Anda menulis test case lain yang memiliki pengulangan tetapi bukan rumah penuh untuk lebih menyempurnakan algoritma Anda.

Anda mungkin atau mungkin tidak melakukan ini dengan literal saat Anda pergi, dan itu baik-baik saja jika Anda melakukannya. Tetapi ide umum adalah mengembangkan algoritma Anda 'secara organik' saat Anda menambahkan lebih banyak kasus.

Erik Dietrich
sumber
Saya telah memperbarui pertanyaan saya untuk menambahkan beberapa informasi tentang mengapa saya mulai dengan pendekatan literal.
Kristof Claes
9
Ini jawaban yang bagus.
Tallseth
1
Terima kasih banyak atas jawaban Anda yang bijaksana dan dijelaskan dengan baik. Sebenarnya sangat masuk akal sekarang karena saya memikirkannya.
Kristof Claes
1
Pengujian menyeluruh tidak berarti menguji setiap kombinasi ... Itu konyol. Untuk kasus khusus ini, ambil satu atau dua rumah penuh dan beberapa rumah yang tidak penuh. Juga setiap kombinasi khusus yang dapat menyebabkan masalah (yaitu, 5 jenis).
Schleis
3
+1 Prinsip-prinsip di balik jawaban ini dijelaskan oleh Robert C. Transformasi Priority Premise Robert cleancoder.posterous.com/the-transformation-priority-premise
Mark Seemann
5

Menguji lima nilai literal tertentu dalam kombinasi tertentu tidaklah "sederhana" bagi otak saya yang demam. Jika solusi untuk suatu masalah benar-benar jelas (hitung apakah Anda memiliki tepat tiga dan tepat dua dari nilai apa pun ), maka tentu saja silakan dan kodekan solusi itu, dan tulis beberapa tes yang akan sangat, sangat tidak mungkin memuaskan secara tidak sengaja dengan jumlah kode yang Anda tulis (yaitu literal yang berbeda dan urutan yang berbeda dari tiga kali lipat dan ganda).

Pepatah TDD benar-benar alat, bukan keyakinan agama. Maksud mereka adalah membuat Anda menulis kode yang benar dan diperhitungkan dengan baik dengan cepat. Jika pepatah jelas menghalangi hal itu, langsung saja maju ke langkah berikutnya. Akan ada banyak bit yang tidak jelas dalam proyek Anda di mana Anda dapat menerapkannya.

Kilian Foth
sumber
5

Jawaban Erik luar biasa, tetapi saya pikir saya mungkin akan berbagi trik dalam penulisan ujian.

Mulai dengan tes ini:

[Test]
public void FullHouseReturnsTrue()
{
    var pairNum = AnyDiceValue();
    var trioNum = AnyDiceValue();

    Assert.That(sut.IsFullHouse(trioNum, pairNum, trioNum, pairNum, trioNum));
}

Tes ini menjadi lebih baik jika Anda membuat Rollkelas daripada melewati 5 params:

[Test]
public void FullHouseReturnsTrue()
{
    var roll = AnyFullHouse();

    Assert.That(sut.IsFullHouse(roll));
}

Itu memberikan implementasi ini:

public bool IsFullHouse(Roll toCheck)
{
    return true;
}

Kemudian tulis tes ini:

[Test]
public void StraightReturnsFalse()
{
    var roll = AnyStraight();

    Assert.That(sut.IsFullHouse(roll), Is.False);
}

Setelah itu berlalu, tulis yang ini:

[Test]
public void ThreeOfAKindReturnsFalse()
{
    var roll = AnyStraight();

    Assert.That(sut.IsFullHouse(roll), Is.False);
}

Setelah itu, saya yakin Anda tidak perlu menulis lagi (mungkin dua pasang, atau mungkin yahtzee, jika Anda pikir itu bukan rumah penuh).

Jelas, terapkan metode Apa pun Anda untuk mengembalikan Rolls acak yang memenuhi kriteria Anda.

Ada beberapa manfaat dari pendekatan ini:

  • Anda tidak perlu menulis tes yang tujuan utamanya adalah untuk menghentikan Anda dari terjebak pada nilai-nilai tertentu
  • Tes-tes tersebut mengomunikasikan maksud Anda dengan sangat baik (kode tes pertama menjerit "rumah penuh apa pun benar")
  • itu membuat Anda cepat ke titik mengerjakan daging masalah
  • kadang-kadang akan melihat kasus yang tidak Anda pikirkan
tallseth
sumber
Jika Anda melakukan pendekatan ini, Anda perlu meningkatkan pesan log Anda dalam pernyataan Assert.That Anda. Pengembang perlu melihat input mana yang menyebabkan kegagalan.
Bringer128
Apakah ini tidak menimbulkan dilema Ayam atau telur? Ketika Anda menerapkan AnyFullHouse (menggunakan TDD juga) tidakkah Anda memerlukan IsFullHouse untuk memverifikasi kebenarannya? Khususnya, jika AnyFullHouse memiliki bug, bug itu dapat direplikasi di IsFullHouse.
waxwing
AnyFullHouse () adalah metode dalam kasus uji. Apakah Anda biasanya TDD kasus uji Anda? Tidak. Juga, jauh lebih mudah untuk membuat contoh acak rumah penuh (atau gulungan lainnya) daripada menguji keberadaannya. Tentu saja, jika pengujian Anda memiliki bug, itu dapat direplikasi dalam kode produksi. Itu berlaku untuk setiap tes.
tallseth
AnyFullHouse adalah metode "pembantu" dalam kasus uji. Jika mereka cukup umum metode penolong diuji juga!
Mark Hurd
Harus IsFullHousebenar-benar kembali truekalau pairNum == trioNum ?
recursion.ninja
2

Saya dapat memikirkan dua cara utama yang akan saya pertimbangkan saat menguji ini;

  1. Tambahkan "beberapa" lebih banyak test case (~ 5) set full-house yang valid, dan jumlah yang sama dari kesalahan yang diharapkan ({1, 1, 2, 3, 3} adalah yang baik. Ingatlah bahwa 5 yang misalnya dapat berupa diakui sebagai "3 dengan jumlah yang sama plus satu pasang" dengan implementasi yang salah). Metode ini mengasumsikan pengembang tidak hanya mencoba untuk lulus tes, tetapi sebenarnya menerapkannya dengan benar.

  2. Uji semua set dadu yang mungkin (hanya ada 252 yang berbeda). Ini tentu saja mengasumsikan Anda memiliki beberapa cara untuk mengetahui apa jawaban yang diharapkan (dalam pengujian ini dikenal sebagai oracle.) Ini bisa menjadi implementasi referensi dari fungsi yang sama, atau manusia. Jika Anda ingin benar-benar teliti, mungkin layak untuk mengkode setiap hasil yang diharapkan secara manual.

Kebetulan, saya pernah menulis AI Yahtzee sekali, yang tentu saja harus tahu aturannya. Anda dapat menemukan kode untuk bagian penilaian skor di sini , harap dicatat bahwa implementasinya adalah untuk versi Skandinavia (Yatzy), dan implementasi kami mengasumsikan dadu diberikan dalam urutan urut.

ansjob
sumber
Pertanyaan sejuta dolar adalah, apakah Anda menurunkan AI Yahtzee menggunakan TDD murni? Taruhan saya adalah Anda tidak bisa; Anda harus menggunakan pengetahuan domain, yang menurut definisi tidak buta :)
Andres F.
Ya, saya kira Anda benar. Ini adalah masalah umum dengan TDD, bahwa kotak uji memerlukan output yang diharapkan kecuali Anda hanya ingin menguji untuk crash yang tidak terduga dan pengecualian yang tidak tertangani.
ansjob
0

Contoh ini sangat merindukan intinya. Kita berbicara tentang satu fungsi langsung di sini bukan desain perangkat lunak. Apakah ini agak rumit? ya, jadi Anda memecahnya. Dan Anda benar-benar tidak menguji setiap input yang mungkin dari 1, 1, 1, 1, 1 hingga 6, 6, 6, 6, 6, 6. Fungsi yang dimaksud tidak memerlukan pesanan, hanya kombinasi, yaitu AAABB.

Anda tidak perlu 200 tes logika terpisah. Anda bisa menggunakan satu set misalnya. Hampir semua bahasa pemrograman memiliki satu bawaan:

Set set;
set.add(a);
set.add(b);
set.add(c);
set.add(d);
set.add(e);

if(set.size() == 2) { // means we *must* be of the form AAAAB or AAABB.
    if(a==b==c==d) // eliminate AAAAB
        return false;
    else
        return true;
}
return false;

Dan jika Anda mendapatkan input yang bukan gulungan Yahtzee yang valid, Anda harus melempar seperti tidak ada hari esok.

Jay Mueller
sumber