Baru-baru ini saya menggunakan metode pabrik. Metodenya adalah membuat objek polos, atau objek yang dibungkus dekorator. Objek yang didekorasi dapat berupa salah satu dari beberapa tipe yang semuanya memperluas StrategyClass.
Dalam pengujian saya, saya ingin memeriksa, apakah kelas objek yang dikembalikan seperti yang diharapkan. Itu mudah ketika objek polos dikembalikan, tetapi apa yang harus dilakukan ketika dibungkus dengan dekorator?
Saya kode dalam PHP sehingga saya bisa gunakan ext/Reflection
untuk mencari tahu kelas objek yang dibungkus, tetapi bagi saya sepertinya hal-hal yang rumit, dan agak menentang aturan TDD.
Sebaliknya saya memutuskan untuk memperkenalkan getClassName()
yang akan mengembalikan nama kelas objek ketika dipanggil dari StrategyClass. Namun ketika dipanggil dari dekorator, itu akan mengembalikan nilai yang dikembalikan dengan metode yang sama pada objek yang didekorasi.
Beberapa kode untuk membuatnya lebih jelas:
interface StrategyInterface {
public function getClassName();
}
abstract class StrategyClass implements StrategyInterface {
public function getClassName() {
return \get_class($this);
}
}
abstract class StrategyDecorator implements StrategyInterface {
private $decorated;
public function __construct(StrategyClass $decorated) {
$this->decorated = $decorated;
}
public function getClassName() {
return $this->decorated->getClassName();
}
}
Dan tes PHPUnit
/**
* @dataProvider providerForTestGetStrategy
* @param array $arguments
* @param string $expected
*/
public function testGetStrategy($arguments, $expected) {
$this->assertEquals(
__NAMESPACE__.'\\'.$expected,
$this->object->getStrategy($arguments)->getClassName()
)
}
//below there's another test to check if proper decorator is being used
Maksud saya di sini adalah: apakah boleh memperkenalkan metode seperti itu, yang tidak memiliki kegunaan lain selain membuat unit test lebih mudah? Entah bagaimana rasanya tidak enak bagiku.
Jawaban:
Pikiranku tidak, Anda tidak boleh melakukan apa-apa hanya karena diperlukan untuk testability. Banyak keputusan yang dibuat orang bermanfaat untuk testability dan testability bahkan mungkin menjadi manfaat utama tetapi harus menjadi keputusan desain yang baik pada jasa lainnya. Ini berarti bahwa beberapa properti yang diinginkan tidak dapat diuji. Contoh lain adalah ketika Anda perlu mengetahui seberapa efisien beberapa rutinitas, misalnya apakah Hashmap Anda menggunakan rentang nilai hash yang terdistribusi secara merata - tidak ada antarmuka eksternal yang dapat memberi tahu Anda hal itu.
Alih-alih berpikir, "Apakah saya mendapatkan kelas strategi yang tepat" berpikir "apakah kelas yang saya dapatkan melakukan apa yang coba diuji oleh spec ini?" Sangat menyenangkan ketika Anda dapat menguji pipa internal tetapi Anda tidak perlu, hanya menguji memutar kenop dan melihat apakah Anda mendapatkan air panas atau dingin.
sumber
Pendapat saya adalah ini - kadang-kadang Anda harus mengerjakan ulang kode sumber Anda sedikit agar lebih dapat diuji. Ini tidak ideal dan seharusnya tidak menjadi alasan untuk mengacaukan antarmuka dengan fungsi yang hanya seharusnya digunakan untuk pengujian sehingga moderasi umumnya adalah kunci di sini. Anda juga tidak ingin berada dalam situasi di mana pengguna kode Anda tiba-tiba menggunakan fungsi antarmuka uji untuk interaksi normal dengan objek Anda.
Cara pilihan saya untuk menangani ini (dan saya harus minta maaf karena saya tidak bisa menunjukkan cara melakukan ini di PHP karena saya terutama kode dalam bahasa C-style) adalah untuk menyediakan fungsi 'tes' dengan cara yang tidak terkena dunia luar oleh objek itu sendiri, tetapi dapat diakses oleh objek yang diturunkan. Untuk tujuan pengujian saya kemudian akan mendapatkan kelas yang akan menangani interaksi dengan objek yang sebenarnya ingin saya uji dan meminta unit test menggunakan objek tertentu. Contoh C ++ akan terlihat seperti ini:
Jenis kelas produksi:
Kelas pembantu tes:
Dengan begitu, Anda setidaknya berada dalam posisi di mana Anda tidak perlu mengekspos fungsi tipe 'test' di objek utama Anda.
sumber
Beberapa bulan yang lalu ketika saya menaruh mesin cuci piring saya yang baru dibeli banyak air keluar dari selang itu, saya menyadari ini mungkin karena fakta itu diuji dengan benar di pabrik asalnya. Tidak jarang untuk melihat lubang pemasangan dan barang-barang pada mesin yang ada di sana - hanya untuk tujuan pengujian di jalur perakitan.
Pengujian itu penting, jika perlu, tambahkan saja sesuatu untuk itu.
Tetapi cobalah beberapa alternatif. Opsi berbasis refleksi Anda tidak terlalu buruk. Anda bisa memiliki aksesor virtual yang dilindungi untuk apa yang Anda butuhkan dan membuat kelas turunan untuk diuji dan ditegaskan. Mungkin Anda dapat membagi kelas Anda dan menguji kelas daun yang dihasilkan secara langsung. Menyembunyikan metode pengujian dengan variabel kompiler dalam kode sumber Anda juga merupakan opsi (saya hampir tidak tahu PHP, tidak yakin apakah itu mungkin dalam PHP).
Dalam konteks Anda, Anda dapat memutuskan untuk tidak menguji komposisi yang tepat di dalam dekorator, tetapi menguji perilaku yang diharapkan dari dekorasi tersebut. Ini mungkin akan lebih fokus pada perilaku sistem yang diharapkan dan tidak terlalu banyak pada spesifikasi teknis (apa pola dekorator membeli Anda dari perspektif fungsional?).
sumber
Saya seorang pemula TDD mutlak, tetapi tampaknya tergantung pada metode yang ditambahkan. Dari apa yang saya mengerti tentang TDD, tes Anda seharusnya "mendorong" pembuatan API Anda sampai tingkat tertentu.
Kapan tidak apa-apa:
Ketika tidak beres:
sumber