Dalam TDD, jika saya menulis test case yang lolos tanpa mengubah kode produksi, apa artinya itu?

17

Ini adalah aturan Robert C. Martin untuk TDD :

  • Anda tidak diperbolehkan untuk menulis kode produksi apa pun kecuali untuk membuat lulus uji unit yang gagal.
  • Anda tidak diperbolehkan menulis lebih dari satu unit tes daripada yang cukup untuk gagal; dan kegagalan kompilasi adalah kegagalan.
  • Anda tidak diperbolehkan menulis kode produksi lebih dari cukup untuk lulus satu unit test gagal.

Ketika saya menulis tes yang tampaknya bermanfaat tetapi lulus tanpa mengubah kode produksi:

  1. Apakah itu berarti saya melakukan sesuatu yang salah?
  2. Haruskah saya menghindari menulis tes seperti itu di masa depan jika dapat membantu?
  3. Haruskah saya meninggalkan tes itu di sana atau menghapusnya?

Catatan: Saya mencoba untuk mengajukan pertanyaan ini di sini: Dapatkah saya mulai dengan tes unit kelulusan? Tapi saya tidak bisa mengartikulasikan pertanyaan dengan cukup baik sampai sekarang.

Daniel Kaplan
sumber
"Bowling Game Kata" yang tertaut pada artikel yang Anda kutip sebenarnya memiliki tes lulus segera sebagai langkah terakhirnya.
jscs

Jawaban:

21

Dikatakan Anda tidak dapat menulis kode produksi kecuali untuk mendapatkan tes unit gagal untuk lulus, bukan bahwa Anda tidak dapat menulis tes yang lolos dari awal. Maksud aturan ini adalah untuk mengatakan "Jika Anda perlu mengedit kode produksi, pastikan Anda menulis atau mengubah tes terlebih dahulu."

Terkadang kita menulis tes untuk membuktikan teori. Tes lulus dan itu membantah teori kami. Kami kemudian tidak menghapus tes. Namun, kami mungkin (mengetahui bahwa kami memiliki dukungan kontrol sumber) memecah kode produksi, untuk memastikan bahwa kami memahami mengapa itu berlalu ketika kami tidak mengharapkannya.

Jika ternyata tes yang valid dan benar, dan itu tidak menduplikasi tes yang ada, biarkan saja.

pdr
sumber
Meningkatkan cakupan tes kode yang ada adalah alasan lain yang sah untuk menulis tes (semoga) lulus.
Jack
13

Ini berarti bahwa:

  1. Anda menulis kode produksi yang memenuhi fitur yang Anda inginkan tanpa menulis tes terlebih dahulu (pelanggaran "TDD agama"), atau
  2. Fitur yang Anda butuhkan sudah terpenuhi oleh kode produksi, dan Anda hanya menulis unit test lain untuk membahas fitur itu.

Situasi terakhir lebih umum daripada yang Anda kira. Sebagai contoh yang sepele dan sepele (tapi masih ilustratif), katakanlah Anda menulis unit test berikut (pseudocode, karena saya malas):

public void TestAddMethod()
{
    Assert.IsTrue(Add(2,3) == 5);
}

Karena yang Anda butuhkan hanyalah hasil dari 2 dan 3 yang ditambahkan bersamaan.

Metode implementasi Anda adalah:

public int add(int x, int y)
{
    return x + y;
}

Tapi katakanlah sekarang saya perlu menambahkan 4 dan 6 bersama-sama:

public void TestAddMethod2()
{
    Assert.IsTrue(Add(4,6) == 10);
}

Saya tidak perlu menulis ulang metode saya, karena sudah mencakup kasus kedua.

Sekarang katakanlah bahwa saya mengetahui bahwa fungsi Tambah saya benar-benar perlu mengembalikan nomor yang memiliki beberapa plafon, katakanlah 100. Saya dapat menulis metode baru yang menguji ini:

public void TestAddMethod3()
{
    Assert.IsTrue(Add(100,100) == 100);
}

Dan tes ini sekarang akan gagal. Sekarang saya harus menulis ulang fungsi saya

public int add(int x, int y)
{
    var a = x + y;
    return a > 100 ? 100 : a;
}

untuk membuatnya lulus.

Akal sehat menentukan bahwa jika

public void TestAddMethod2()
{
    Assert.IsTrue(Add(4,6) == 10);
}

melewati, Anda tidak sengaja membuat metode Anda gagal hanya agar Anda dapat memiliki tes gagal sehingga Anda dapat menulis kode baru untuk membuat lulus ujian itu.

Robert Harvey
sumber
5
Jika Anda mengikuti contoh Martin sepenuhnya (dan dia tidak selalu menyarankan Anda melakukannya), untuk add(2,3)lulus, Anda benar-benar akan kembali 5. Hard-coded. Kemudian Anda akan menulis tes add(4,6)yang akan memaksa Anda untuk menulis kode produksi yang membuatnya lulus tanpa melanggar add(2,3)pada saat yang sama. Anda akan berakhir dengan return x + y, tetapi Anda tidak akan memulainya. Dalam teori. Tentu saja, Martin (atau mungkin orang lain, saya tidak ingat) suka memberikan contoh-contoh seperti itu untuk pendidikan, tetapi tidak berharap Anda benar-benar menulis kode sepele seperti itu.
Anthony Pegram
1
@tTTT, umumnya, jika saya ingat dari buku Martin dengan benar, test case kedua biasanya cukup untuk membuat Anda menulis solusi umum untuk metode sederhana (dan, pada kenyataannya, Anda memang akan membuatnya bekerja pertama kali). Tidak perlu sepertiga.
Anthony Pegram
2
@tTyT, maka Anda akan terus menulis tes sampai Anda melakukannya. :)
Anthony Pegram
4
Ada kemungkinan ketiga, dan itu bertentangan dengan contoh Anda: Anda menulis tes rangkap. Jika Anda mengikuti TDD "religius", maka tes baru yang lolos selalu merupakan tanda merah. Mengikuti KERING , Anda seharusnya tidak pernah menulis dua tes yang pada dasarnya menguji hal yang sama.
congusbongus
1
"Jika kamu mengikuti contoh Martin sepenuhnya (dan dia tidak menyarankan kamu melakukannya), untuk menambahkan (2,3) pass, kamu benar-benar akan mengembalikan 5. Hard-coded." - ini adalah sedikit TDD ketat yang selalu cocok dengan saya, gagasan bahwa Anda menulis kode yang Anda tahu salah dengan harapan akan ada tes yang akan datang dan membuktikannya. Bagaimana jika tes masa depan itu tidak pernah ditulis, untuk beberapa alasan, dan rekan menganggap "all-tes-hijau" menyiratkan "semua-kode-benar"?
Julia Hayward
2

Tes lulus tetapi Anda tidak salah. Saya pikir, itu terjadi karena kode produksi bukan TDD dari awal.

Mari kita anggap TDD kanonik (?). Tidak ada kode produksi tetapi beberapa test case (yang tentu saja selalu gagal). Kami menambahkan kode produksi untuk lulus. Kemudian berhenti di sini untuk menambahkan lebih banyak test case yang gagal. Sekali lagi tambahkan kode produksi untuk lulus.

Dengan kata lain, tes Anda bisa menjadi semacam tes fungsionalitas bukan tes unit TDD sederhana. Itu selalu merupakan aset berharga untuk kualitas produk.

Saya pribadi tidak suka aturan totaliter dan tidak manusiawi seperti itu; (

9dan
sumber
2

Sebenarnya masalah yang sama muncul di dojo tadi malam.

Saya melakukan riset cepat tentang itu. Inilah yang saya pikirkan:

Pada dasarnya itu tidak dilarang secara eksplisit oleh aturan TDD. Mungkin diperlukan beberapa tes tambahan untuk membuktikan bahwa suatu fungsi berfungsi dengan benar untuk input yang digeneralisasi. Dalam hal ini praktik TDD dikesampingkan hanya untuk sementara waktu. Perhatikan bahwa meninggalkan praktik TDD segera tidak harus melanggar aturan TDD selama tidak ada kode produksi yang ditambahkan untuk sementara waktu.

Tes tambahan dapat ditulis selama tidak berlebihan. Praktik yang baik adalah melakukan pengujian partisi kelas kesetaraan. Itu berarti bahwa kasus tepi dan setidaknya satu kasus dalam untuk setiap kelas kesetaraan diuji.

Namun, satu masalah yang dapat terjadi dengan pendekatan ini adalah bahwa jika tes lulus dari awal tidak dapat dipastikan bahwa tidak ada positif palsu. Artinya mungkin ada tes yang lulus karena tes tidak diterapkan dengan benar dan bukan karena kode produksi bekerja dengan benar. Untuk mencegah hal ini, kode produksi harus diubah sedikit untuk memecahkan tes. Jika ini membuat tes gagal, tes kemungkinan besar dilaksanakan dengan benar dan kode produksi dapat diubah kembali untuk membuat tes lulus lagi.

Jika Anda hanya ingin berlatih TDD ketat, Anda mungkin tidak menulis tes tambahan apa pun yang lulus dari awal. Di sisi lain dalam lingkungan pengembangan perusahaan, seseorang harus meninggalkan praktik TDD jika tes tambahan tampak bermanfaat.

Leifbattermann
sumber
0

Tes yang lolos tanpa mengubah kode produksi tidak selalu buruk, dan seringkali diperlukan untuk menggambarkan persyaratan tambahan atau batas kasus. Selama tes Anda "tampaknya bermanfaat", seperti yang Anda katakan, Anda tetap melakukannya, pertahankan.

Di mana Anda mendapat masalah adalah ketika Anda menulis tes yang sudah lulus sebagai pengganti untuk benar-benar memahami ruang masalah.

Kita dapat membayangkan pada dua ekstrem: satu programmer yang menulis sejumlah besar tes "berjaga-jaga" satu menangkap bug; dan programmer kedua yang dengan hati-hati menganalisis ruang masalah sebelum menulis sejumlah kecil tes. Katakanlah keduanya mencoba menerapkan fungsi nilai absolut.

Programmer pertama menulis:

assert abs(-88888) == 88888
assert abs(-12345) == 12345
assert abs(-5000) == 5000
assert abs(-32) == 32
assert abs(46) == 46
assert abs(50) == 50
assert abs(5001) == 5001
assert abs(999999) == 999999
...

Programmer kedua menulis:

assert abs(-1) == 1
assert abs(0) == 0
assert abs(1) == 1

Implementasi programmer pertama mungkin menghasilkan:

def abs(n):
    if n < 0:
        return -n
    elif n > 0:
        return n

Implementasi programmer kedua mungkin menghasilkan:

def abs(n):
    if n < 0:
        return -n
    else:
        return n

Semua tes lulus, tetapi programmer pertama tidak hanya menulis beberapa tes yang berlebihan (tidak perlu memperlambat siklus pengembangan mereka), tetapi juga gagal untuk menguji kasus batas ( abs(0)).

Jika Anda menemukan diri Anda menulis tes yang lulus tanpa mengubah kode produksi, tanyakan pada diri sendiri apakah tes Anda benar-benar menambah nilai atau apakah Anda perlu menghabiskan lebih banyak waktu untuk memahami ruang masalah.

pemikir
sumber
Nah, programmer kedua jelas ceroboh dengan tes juga, karena rekan kerjanya didefinisikan ulang abs(n) = n*ndan lulus.
Eiko
@ Eiko Anda benar sekali. Menulis terlalu sedikit tes dapat menggigit Anda sama buruknya. Pemrogram kedua terlalu pelit karena tidak setidaknya menguji abs(-2). Seperti halnya segalanya, moderasi adalah kuncinya.
pemikir