Jika saya memiliki fungsi dalam kode saya seperti:
class Employee{
public string calculateTax(string name, int salary)
{
switch (name)
{
case "Chris":
doSomething($salary);
case "David":
doSomethingDifferent($salary);
case "Scott":
doOtherThing($salary);
}
}
Biasanya saya akan menolak ini untuk menggunakan Ploymorphism menggunakan kelas pabrik dan pola strategi:
public string calculateTax(string name)
{
InameHandler nameHandler = NameHandlerFactory::getHandler(name);
nameHandler->calculateTax($salary);
}
Sekarang jika saya menggunakan TDD maka saya akan memiliki beberapa tes yang berfungsi pada aslinya calculateTax()
sebelum refactoring.
ex:
calculateTax_givenChrisSalaryBelowThreshold_Expect111(){}
calculateTax_givenChrisSalaryAboveThreshold_Expect111(){}
calculateTax_givenDavidSalaryBelowThreshold_Expect222(){}
calculateTax_givenDavidSalaryAboveThreshold_Expect222(){}
calculateTax_givenScottSalaryBelowThreshold_Expect333(){}
calculateTax_givenScottSalaryAboveThreshold_Expect333(){}
Setelah refactoring saya akan memiliki kelas Pabrik NameHandlerFactory
dan setidaknya 3 implementasi InameHandler
.
Bagaimana saya harus melanjutkan untuk memperbaiki tes saya? Haruskah saya menghapus unit test claculateTax()
dari EmployeeTests
dan membuat kelas Test untuk setiap implementasi InameHandler
?
Haruskah saya menguji kelas Pabrik juga?
salary
ke fungsicalculateTax()
telah ditambahkan. Dengan cara ini saya pikir saya akan menduplikasi kode tes untuk fungsi asli dan 3 implementasi dari kelas strategi.Saya akan mulai dengan mengatakan bahwa saya bukan ahli dalam pengujian TDD atau unit, tapi inilah cara saya akan menguji ini (saya akan menggunakan kode pseudo-like):
Jadi saya akan menguji bahwa
calculateTax()
metode kelas karyawan dengan benar memintaNameHandlerFactory
untukNameHandler
dan kemudian memanggilcalculateTax()
metode yang dikembalikanNameHandler
.sumber
Employee.calculateTax()
metode ini. Dengan begitu Anda tidak perlu menambahkan tes Karyawan tambahan saat Anda memperkenalkan NameHandler baru.Anda mengambil satu kelas (karyawan yang melakukan segalanya) dan membuat 3 kelompok kelas: pabrik, karyawan (yang hanya berisi strategi) dan strategi.
Jadi buat 3 kelompok tes:
Anda tentu saja dapat membuat tes otomatis untuk seluruh shebang, tetapi ini sekarang lebih seperti tes integrasi dan harus diperlakukan seperti itu.
sumber
Sebelum menulis kode apa pun, saya akan mulai dengan tes untuk Pabrik. Mengejek hal-hal yang saya butuhkan saya akan memaksakan diri untuk berpikir tentang implementasi dan penggunaan.
Daripada saya akan mengimplementasikan Pabrik dan melanjutkan dengan tes untuk setiap implementasi dan akhirnya implementasi sendiri untuk tes tersebut.
Akhirnya saya akan menghapus tes lama.
sumber
Pendapat saya adalah bahwa Anda tidak boleh melakukan apa pun, artinya Anda tidak perlu menambahkan tes baru.
Saya menekankan bahwa ini adalah pendapat, dan itu sebenarnya tergantung pada cara Anda memahami harapan dari objek. Apakah Anda pikir pengguna kelas ingin menyediakan strategi untuk perhitungan pajak? Jika dia tidak peduli, maka tes harus mencerminkan itu, dan perilaku yang tercermin dari tes unit harus bahwa mereka seharusnya tidak peduli bahwa kelas sudah mulai menggunakan objek strategi untuk menghitung pajak.
Saya benar-benar mengalami masalah ini beberapa kali ketika menggunakan TDD. Saya pikir alasan utamanya adalah bahwa objek strategi bukanlah ketergantungan alami, sebagai lawan dari ketergantungan arsitektur seperti sumber daya eksternal (file, DB, layanan jarak jauh, dll.). Karena ini bukan ketergantungan alami, saya biasanya tidak mendasarkan perilaku kelas saya pada strategi ini. Naluriku adalah bahwa aku hanya boleh mengubah tesku jika harapan dari kelasku telah berubah.
Ada pos yang bagus dari Paman Bob, yang berbicara persis tentang masalah ini ketika menggunakan TDD.
Saya berpikir bahwa kecenderungan untuk menguji setiap kelas yang terpisah adalah apa yang membunuh TDD. Seluruh keindahan TDD adalah bahwa Anda menggunakan tes untuk memacu skema desain dan bukan sebaliknya.
sumber