Apakah mengikuti satu pernyataan per tes konsistensi bodoh dalam kasus ini?

10

Saya memiliki kelas yang saya uji. Kelas memiliki fungsi:apply(List<IRule> rules, List<ITarget> targets);

Dalam satu pengujian, saya ingin memastikan bahwa setiap target telah lulus ke satu aturan, a la:

rule1.AssertWasCalled(fnord => fnord.Test(target1));
rule1.AssertWasCalled(fnord => fnord.Test(target2));
rule1.AssertWasCalled(fnord => fnord.Test(target3));

Tampaknya bagi saya bahwa membatasi diri saya pada pernyataan pernyataan tunggal akan cukup hobgoblin . Apakah saya benar dalam asumsi ini, atau adakah cara lain yang bisa saya nyatakan bahwa setiap target sebenarnya telah diuji?

Wayne Werner
sumber
Saya bisa melihat fnord!
Ross Patterson

Jawaban:

15

Tiga menegaskan pada dasarnya satu tes. Anda menguji perilaku metode pada koleksi, untuk memastikan bahwa setiap item telah menjadi parameter untuk panggilan tertentu (yaitu bahwa setiap item telah diproses dengan benar).

Pengaturan data tiga kali dan dalam tiga metode yang berbeda adalah pemborosan dan kurang dapat dibaca daripada alternatif memiliki beberapa menegaskan.

Pernyataan tunggal "aturan" lebih lanjut tentang membuat pernyataan dari jenis yang berbeda dalam metode yang sama (pada dasarnya menguji hal-hal yang berbeda), yang tidak benar-benar berlaku dalam kasus ini, di mana Anda menguji perilaku tunggal.

Oded
sumber
3
Memang: aturannya lebih dari satu penegasan logis per Unit Test. Anda dapat mengelompokkannya menjadi satu tingkat yang lebih tinggi menyatakan bahwa Anda dapat menggunakan kembali dalam tes yang berbeda.
Laurent Bourgault-Roy
5

Ini adalah keyakinan saya bahwa pernyataan satu per aturan pengujian ini ada untuk menjaga tes Anda fokus pada satu masalah. Jika Anda menguji 20 hal dalam satu tes, sangat sulit untuk mengetahui apa cakupan Anda. Anda tahu itu menyebabkan masalah ketika Anda tidak dapat memberi nama metode pengujian tanpa kata dan di dalamnya. Misalnya, jika metode pengujian Anda akan lebih akurat disebut testFooIsTrueAndDbExistsAndBarIsNullAndAnExceptionDoesntOccur(), Anda mungkin menguji terlalu banyak dalam satu pengujian.

Dalam kasus Anda, saya pikir mungkin oke untuk menegaskan tiga kali. Jika Anda ingin membuat kode Anda lebih mudah dibaca, Anda bisa mengekstrak ketiga pernyataan tersebut ke dalam metode yang dinamai assertWasCalledOnTargets(...).

Daniel Kaplan
sumber
3

Sebagai contoh khusus Anda, Anda bisa lolos dengan "satu" pernyataan tegas jika Anda melakukan sesuatu seperti:

foreach target in targets
{
     rule1.AssertWasCalled(fnord => fnord.Test(target))
}

Itu yang saya lakukan untuk menghindari perasaan bersalah karena memiliki beberapa pernyataan dalam satu tes.

Jeff B
sumber
Saya pernah melakukan ini sebelumnya. Bukan cara yang buruk untuk pergi. Sangat mudah dibaca, dan Anda dapat memahami sifat dari apa yang dilakukannya.
CokoBWare
1

Saya telah berjuang dengan yang satu ini juga.

Purist (di saya) bersikeras pada satu menegaskan per tes jadi saya akan tahu * persis * di mana semuanya meledak.

Dan kemudian saya menemukan diri saya memotong / menempel banyak kode setup tes yang sama, redundan. Setelah lapisan ketiga atau keempat ini, Anda mulai berkata "Oy! Cukup!"

Kompromi saya adalah menemukan aspek-aspek yang "tidak pernah" pecah. Dan saya akan melapisi potongan-potongan itu bersama-sama dan kemudian menambahkan satu elemen baru yang bisa pecah. Untuk lebih jelasnya, meletakkan beberapa area yang mudah menguap dalam satu pengujian akan menjadi pelanggaran kompromi ini.


sumber
1
Anda harus memeriksa Assume. Saya baru tahu hari ini.
Wayne Werner
1

Jika kode pengaturan untuk target1berbeda dari kode pengaturan untuk target2, jenis pemotongan sudut ini pada akhirnya cenderung menyebabkan kode inisialisasi tes terlalu lama. Ini pada gilirannya adalah kekacauan atau akhirnya menjadi refactored dan digunakan kembali. Jika tes Anda cukup kompleks untuk membenarkan refactoring mereka, tes Anda mungkin menguji lebih dari satu hal.

Jika kode pengaturan untuk setiap target pada dasarnya sama, membagi tes Anda menjadi beberapa tes individu mungkin berlebihan.

Jika target1dan target2merupakan implementasi berbeda dari antarmuka yang sama, Anda seharusnya menambahkan tes unit ke antarmuka (dan memungkinkan kerangka uji Anda untuk menghasilkan tes untuk setiap implementasi antarmuka itu).

Brian
sumber