Bagaimana saya bisa mulai menggunakan TDD untuk membuat kode beberapa fungsi sederhana?


9

Saya pada dasarnya memiliki inti dari TDD. Saya menjual bahwa ini berguna dan saya punya perintah yang wajar dari kerangka kerja MSTEST. Namun, sampai saat ini saya belum dapat lulus untuk menggunakannya sebagai metode pengembangan utama. Sebagian besar, saya menggunakannya sebagai pengganti untuk menulis aplikasi konsol sebagai driver tes (pendekatan tradisional saya).

Hal yang paling berguna bagi saya adalah cara menyerap peran pengujian regresi.

Saya belum membangun apa pun yang secara khusus mengisolasi berbagai perilaku yang dapat diuji, yang merupakan bagian besar lain dari gambar yang saya tahu.

Jadi pertanyaan ini adalah untuk meminta petunjuk tentang apa tes pertama (s) yang saya tulis untuk tugas pengembangan berikut: Saya ingin menghasilkan kode yang merangkum pelaksanaan tugas dengan cara produsen / konsumen.

Saya berhenti dan memutuskan untuk menulis pertanyaan ini setelah saya menulis kode ini (bertanya-tanya apakah saya benar-benar dapat menggunakan TDD nyata saat ini)

Kode:

interface ITask
{
    Guid TaskId { get; }
    bool IsComplete { get; }
    bool IsFailed { get; }
    bool IsRunning { get; }
}

interface ITaskContainer
{
    Guid AddTask(ICommand action);
}

interface ICommand
{
    string CommandName { get; }
    Dictionary<string, object> Parameters { get; }
    void Execute();
}

Anda harus menulis tes terlebih dahulu dan MAKA antarmuka! Seluruh idenya adalah bahwa TDD adalah untuk API Anda.

1
Bagaimana cara seseorang menulis tes untuk antarmuka yang belum ada? Mereka bahkan tidak akan mengkompilasi.
Robert Harvey

5
Itu tes gagal pertama Anda.
cori

Jawaban:


10

Dimulai dengan konsep ini:
1) Mulailah dengan perilaku yang Anda inginkan. Tulis tes untuk itu. Lihat tes gagal.
2) Tulis kode yang cukup untuk lulus tes. Lihat semua tes lulus.
3) Cari kode redundan / ceroboh -> refactor. Lihat tes masih lulus. Kebagian 1

Jadi pada # 1, katakanlah Anda ingin membuat perintah baru (saya ingin tahu bagaimana perintah akan bekerja, jadi bersabarlah dengan saya). (Juga, saya akan sedikit pragmatis daripada TDD ekstrim)

Perintah baru disebut MakeMyLunch, jadi Anda pertama kali membuat tes untuk membuat instance dan mendapatkan nama perintah:

@Test
public void instantiateMakeMyLunch() {
   ICommand command = new MakeMyLunchCommand();
   assertEquals("makeMyLunch",command.getCommandName());
}

Ini gagal, memaksa Anda untuk membuat kelas perintah baru dan mengembalikannya (Purist akan mengatakan ini adalah dua putaran TDD, bukan 1). Jadi Anda membuat kelas dan menerapkannya antarmuka ICommand, termasuk mengembalikan nama perintah. Menjalankan semua tes sekarang menunjukkan semua lulus, jadi Anda melanjutkan untuk mencari peluang refactoring. Mungkin tidak ada.

Jadi selanjutnya Anda ingin mengimplementasikannya mengeksekusi. Jadi, Anda harus bertanya: bagaimana saya tahu bahwa "MakeMyLunch" berhasil "membuat makan siang saya". Apa perubahan dalam sistem karena operasi ini? Bisakah saya menguji ini?

Misalkan mudah untuk menguji:

@Test
public void checkThatMakeMyLunchIsSuccessful() {
   ICommand command = new MakeMyLunchCommand();
   command.execute();
   assertTrue( Lunch.isReady() );
}

Di lain waktu, ini lebih sulit, dan apa yang benar-benar ingin Anda lakukan adalah menguji tanggung jawab subjek-under-test (MakeMyLunchCommand). Mungkin tanggung jawab MakeMyLunchCommand adalah berinteraksi dengan Kulkas dan Microwave. Jadi untuk mengujinya Anda bisa menggunakan Kulkas tiruan dan tiruan Microwave. [dua kerangka contoh mock adalah Mockito dan nMock atau lihat di sini .]

Dalam hal ini Anda akan melakukan sesuatu seperti kode pseudo berikut:

@Test
public void checkThatMakeMyLunchIsSuccessful() {
   Fridge mockFridge = mock(Fridge);
   Microwave mockMicrowave = mock(Microwave);
   ICommand command = new MakeMyLunchCommand( mockFridge, mockMicrowave );
   command.execute();
   mockFramework.assertCalled( mockFridge.removeFood );
   mockFramework.assertCalled( microwave.turnon );
}

Purist mengatakan uji tanggung jawab kelas Anda - interaksinya dengan kelas lain (apakah perintah membuka kulkas dan menyalakan microwave?).

Pragmatis itu mengatakan tes untuk sekelompok kelas dan tes untuk hasilnya (apakah makan siang Anda siap?).

Temukan keseimbangan yang tepat yang berfungsi untuk sistem Anda.

(Catatan: pertimbangkan bahwa mungkin Anda tiba di struktur antarmuka terlalu dini. Mungkin Anda dapat membiarkan ini berkembang saat Anda menulis pengujian dan implementasi unit Anda, dan pada langkah # 3 Anda "memperhatikan" peluang antarmuka umum).


jika saya belum menulis antarmuka saya sebelumnya, pertanyaan apa yang akan mengarah pada pembuatan metode Execute () - beberapa upaya awal saya untuk TDD terhenti ketika saya tidak memiliki "langkah" untuk merangsang fungsi tambahan - saya mendapatkan Perasaan ada masalah ayam / telur laten yang harus dihindari
Aaron Anodide

1
Pertanyaan bagus! Jika satu-satunya perintah yang Anda buat adalah "MakeMyLunchCommand" metode ini mungkin sudah dimulai dengan ".makeMyLunch ()". Yang akan baik-baik saja. Kemudian Anda membuat perintah lain ("NapCommand.takeNap ()"). Masih tidak ada masalah dengan metode yang berbeda. Kemudian Anda mulai menggunakannya di ekosistem Anda, yang kemungkinan besar Anda dipaksa untuk menggeneralisasi ke antarmuka ICommand. Secara umum, Anda sering menunda generalisasi hingga saat bertanggung jawab terakhir, karena YAGNI [ en.wikipedia.org/wiki/You_ain't_gonna_need_it ] =) Di lain waktu, Anda tahu Anda akan sampai di sana sehingga Anda mulai dengan itu.
jayraynet

(Juga asumsi di sini adalah bahwa Anda menggunakan IDE modern yang membuat refactoring hal-hal seperti nama metode sepele)
jayraynet

1
terima kasih lagi untuk sarannya, ini semacam tonggak bagi saya untuk akhirnya melihat semua bagian dan bagaimana mereka cocok - dan ya, refactor cepat ada di kotak peralatan saya
Aaron Anodide
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.