Apakah boleh memperkenalkan metode yang hanya digunakan selama pengujian unit?


12

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/Reflectionuntuk 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.


Mungkin Pertanyaan ini akan memberikan lebih banyak wawasan tentang pertanyaan Anda, programmers.stackexchange.com/questions/231237/… , saya percaya itu tergantung pada penggunaan dan apakah metode akan sangat membantu dalam pengujian unit dan debugging aplikasi apa pun yang Anda kembangkan .. .
Clement Mark-Aaba

Jawaban:


13

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.


+1 OP menggambarkan pengujian unit 'kotak bersih', bukan pengujian fitur TDD
Steven A. Lowe

1
Saya melihat pointh ere, meskipun saya agak enggan untuk menambahkan pengujian algoritma StrategyClass ketika saya ingin menguji apakah metode pabrik melakukan pekerjaannya. Jenis ini istirahat isolasi IMHO. Alasan lain yang ingin saya hindari adalah bahwa kelas-kelas khusus ini beroperasi pada basis data, jadi mengujinya membutuhkan mocking / stubbing tambahan.
Mchl

Di sisi lain dan dalam terang pertanyaan ini: programmers.stackexchange.com/questions/86656/… ketika kita membedakan "tes TDD" dari "tes unit" ini menjadi sangat baik (masih menjadi pipa basis data meskipun: P)
Mchl

Jika Anda menambahkan metode, mereka menjadi bagian dari kontrak dengan pengguna Anda. Anda akan berakhir dengan coders yang memanggil fungsi dan cabang uji-hanya berdasarkan hasil. Secara umum, saya lebih suka mengekspos sesedikit mungkin dari kelas.
BillThor

5

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:

class ProdObj
{
  ...
  protected:
     virtual bool checkCertainState();
};

Kelas pembantu tes:

class TestHelperProdObj : public ProdObj
{
  ...
  public:
     virtual bool checkCertainState() { return ProdObj::checkCertainState(); }
};

Dengan begitu, Anda setidaknya berada dalam posisi di mana Anda tidak perlu mengekspos fungsi tipe 'test' di objek utama Anda.


Pendekatan yang menarik. Saya perlu melihat bagaimana saya bisa beradaptasi ini
Mchl

3

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?).


1

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:

  • Selama metode tersebut tidak merusak enkapsulasi dan melayani tujuan yang sejalan dengan tanggung jawab objek.

Ketika tidak beres:

  • Jika metode ini tampak seperti sesuatu yang tidak akan pernah berguna, atau tidak masuk akal dalam kaitannya dengan metode antarmuka lain, itu mungkin tidak konsisten atau membingungkan. Pada titik itu, itu akan menambah pemahaman saya tentang objek itu.
  • Contoh Jeremy, "... ketika Anda perlu tahu 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 mengenai hal itu."
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.