TDD dengan pola repositori

10

Dalam proyek baru saya, saya memutuskan untuk mencoba dengan TDD. Dan pada awalnya saya mengalami masalah. Hal pertama yang ingin saya lakukan dalam aplikasi saya adalah memberi kemampuan membaca data dari sumber data. Untuk tujuan ini, saya ingin menggunakan pola repositori. Dan sekarang:

  • Jika tes untuk implementasi nyata dari antarmuka repositori, saya akan menguji kelas yang memiliki akses ke database, dan saya tahu bahwa saya harus menghindari itu.
  • Jika tes bukan untuk implementasi nyata dari pola repositori, saya akan menguji dengan baik ... hanya mengejek. Tidak akan ada bagian dari kode produksi yang diuji dalam unit test tersebut.

Saya memikirkan hal ini sejak dua hari dan masih belum dapat memberikan solusi yang masuk akal. Apa yang harus saya lakukan?

Thaven
sumber

Jawaban:

11

Apa yang dilakukan repositori adalah menerjemahkan dari domain Anda ke kerangka kerja DAL Anda, seperti NHibernate atau Doctrine, atau kelas yang menjalankan SQL Anda. Ini berarti bahwa repositori Anda akan memanggil metode pada framework tersebut untuk melakukan tugasnya: repositori Anda membangun kueri yang diperlukan untuk mengambil data. Jika Anda tidak menggunakan kerangka kerja ORM (saya harap Anda ...), repositori akan menjadi tempat di mana pernyataan SQL mentah dibuat.

Yang paling dasar dari metode ini adalah save: dalam kebanyakan kasus ini hanya akan melewatkan objek dari repositori ke unit kerja (atau sesi).

public void Save(Car car)
{
    session.Save(car);
}

Tapi mari kita lihat contoh lain, misalnya mengambil mobil dengan ID-nya. Mungkin terlihat seperti

public function GetCarWithId(String id)
{
    return Session.QueryOver<Car>()
                    .Where(x => x.Id == id)
                    .SingleOrDefault();
}

Masih tidak terlalu rumit, tetapi Anda dapat membayangkan dengan berbagai kondisi (dapatkan saya semua mobil yang dibuat setelah 2010 untuk semua merek dalam grup 'Volkswagen') ini menjadi rumit. Jadi dalam mode TDD sejati Anda perlu menguji ini. Ada beberapa cara untuk melakukan ini.

Opsi 1: Mengolok-olok panggilan yang dibuat untuk kerangka kerja ORM

Tentu, Anda dapat mengejek objek Sesi dan hanya menegaskan bahwa panggilan yang tepat telah dibuat. Saat ini menguji repositori, itu tidak benar-benar didorong oleh tes karena Anda hanya menguji repositori itu secara internal seperti yang Anda inginkan. Tes pada dasarnya mengatakan 'kodenya akan terlihat seperti ini'. Meski begitu, ini adalah pendekatan yang valid tetapi rasanya seperti tes ini memiliki nilai yang sangat kecil.

Opsi 2: (Kembali) membangun database dari tes

Beberapa kerangka kerja DAL memberi Anda kemampuan untuk membangun struktur lengkap basis data berdasarkan file pemetaan yang Anda buat untuk memetakan domain ke tabel. Untuk kerangka kerja ini cara untuk menguji repositori sering membuat database dengan database di-memori pada langkah pertama pengujian dan menambahkan objek menggunakan kerangka-DAL ke database di-memori. Setelah ini, Anda dapat menggunakan repositori pada basis data di dalam memori untuk menguji apakah metode tersebut bekerja. Tes ini lebih lambat, tetapi sangat valid dan mendorong pengujian Anda. Itu memang membutuhkan kerja sama dari kerangka kerja DAL Anda.

Opsi 3: Tes pada database aktual

Pendekatan lain adalah menguji pada basis data aktual dan mengisolasi yang belum terdaftar. Anda dapat melakukan ini dengan beberapa cara: mengelilingi pengujian Anda dengan transaksi, bersihkan secara manual (tidak akan merekomendasikan pemeliharaan yang sangat sulit), buat kembali sepenuhnya basis data setelah setiap langkah ... Tergantung pada aplikasi yang Anda bangun ini mungkin atau mungkin tidak layak. Dalam aplikasi saya, saya benar-benar dapat membangun database pengembangan lokal dari kontrol sumber dan unittests saya pada repositori menggunakan transaksi untuk sepenuhnya mengisolasi tes satu sama lain (transaksi terbuka, memasukkan data, repositori tes, transaksi rollback). Setiap build pertama-tama membuat basis data pengembangan lokal dan kemudian melakukan unittests transaksi-terisolasi untuk repositori pada database pembangunan lokal itu. Itu'

Jangan menguji DAL

Jika Anda menggunakan kerangka kerja DAL seperti NHibernate, hindari kebutuhan untuk menguji kerangka kerja itu. Anda dapat menguji file pemetaan Anda dengan menyimpan, mengambil dan kemudian membandingkan objek domain untuk memastikan semuanya baik-baik saja (pastikan untuk menonaktifkan segala jenis caching) tetapi tidak seperti yang diperlukan karena banyak tes lain yang harus Anda tulis. Saya cenderung melakukan ini kebanyakan untuk koleksi pada orang tua dengan kondisi pada anak-anak.

Saat menguji kembalinya repositori Anda, Anda bisa memeriksa untuk melihat apakah beberapa properti yang mengidentifikasi objek domain Anda cocok. Ini bisa menjadi id tetapi dalam tes sering kali lebih menguntungkan untuk memeriksa properti yang dapat dibaca manusia. Dalam 'dapatkan saya semua mobil yang dibuat setelah 2010 ....' ini bisa dengan mudah memeriksa bahwa lima mobil dikembalikan dan plat nomornya adalah 'masukkan daftar di sini'. Manfaat tambahan adalah itu memaksa Anda untuk berpikir tentang penyortiran DAN tes Anda secara otomatis memaksa penyortiran. Anda akan terkejut betapa banyak aplikasi yang mengurutkan beberapa kali (kembali diurutkan dari database, mengurutkan sebelum membuat objek tampilan dan kemudian mengurutkan objek tampilan, semua pada properti yang sama untuk berjaga-jaga ) atau secara implisit menganggap jenis repositori dan secara tidak sengaja menghapus bahwa beberapa hal di sepanjang jalan, melanggar UI.

'Tes unit' hanyalah sebuah nama

Menurut pendapat saya, tes unit sebagian besar tidak boleh mengenai database. Anda membangun aplikasi sehingga setiap bagian kode yang membutuhkan data dari sumber melakukan ini dengan repositori, dan repositori itu disuntikkan sebagai dependensi. Ini memungkinkan untuk mengejek mudah dan semua kebaikan TDD yang Anda inginkan. Tetapi pada akhirnya Anda ingin memastikan bahwa repositori Anda melakukan tugas mereka dan jika cara termudah untuk melakukannya adalah mengenai database, ya, biarlah. Saya sudah lama melepaskan anggapan bahwa 'unit test tidak boleh menyentuh database' dan mengetahui bahwa ada alasan yang sangat nyata untuk melakukan ini. Tetapi hanya jika Anda dapat melakukan ini secara otomatis dan berulang kali. Dan cuaca yang kita sebut tes semacam itu 'tes unit' atau 'tes integrasi' sedang diperdebatkan.

JDT
sumber
3
Tes unit dan tes integrasi memiliki tujuan yang berbeda. Nama-nama untuk tes ini tidak hanya dekoratif; mereka juga deskriptif.
Robert Harvey
9
  1. Jangan menguji metode penyimpanan yang sepele atau jelas.

    Jika metodenya operasi CRUD sepele, semua Anda benar-benar menguji adalah apakah parameter dipetakan dengan benar. Jika Anda memiliki tes integrasi, kesalahan tersebut akan segera menjadi jelas.

    Ini adalah prinsip yang sama yang berlaku untuk sifat sepele, seperti ini:

    public property SomeProperty
    {
        get { return _someProperty; }
        set { _someProperty = value; }
    }
    

    Anda tidak mengujinya, karena tidak ada yang bisa diuji. Tidak ada validasi atau logika lain di properti yang perlu diverifikasi.

  2. Jika Anda masih ingin menguji metode itu ...

    Mengolok-olok adalah cara untuk melakukannya. Ingat, ini adalah Tes Unit. Anda tidak menguji database dengan tes unit; itulah tujuan dari Tes Integrasi.

Informasi Lebih Lanjut
Stack Lengkap, Bagian 3: Membangun Repositori menggunakan TDD (mulai menonton sekitar 16 menit).

Robert Harvey
sumber
3
Tentu saya mengerti ini. Namun, jika pendekatan TDD, saya tidak boleh menulis kode apa pun jika saya tidak memiliki tes untuk kode ini terlebih dahulu, kan?
Thaven
1
@Thaven - ada serangkaian video di youtube berjudul "is tdd dead?". Perhatikan mereka. Mereka membahas banyak poin menarik, salah satunya adalah gagasan bahwa menerapkan TDD di setiap tingkat aplikasi Anda belum tentu merupakan ide terbaik. "tidak ada kode tanpa tes gagal" adalah posisi yang terlalu ekstrim, adalah salah satu kesimpulan.
Jules
2
@ Jules: Apa tl; dw?
Robert Harvey
1
@RobertHarvey Sulit diringkas, tetapi poin terpenting adalah memperlakukan TDD sebagai agama yang harus selalu diperhatikan adalah kesalahan. Pilihan untuk menggunakannya adalah bagian dari trade-off dan Anda perlu mempertimbangkan bahwa (1) Anda dapat bekerja lebih cepat tanpa itu pada beberapa masalah dan (2) itu mungkin mendorong Anda ke arah solusi yang lebih kompleks daripada yang Anda butuhkan, terutama jika Anda menemukan diri Anda menggunakan banyak ejekan.
Jules
1
+1 untuk poin # 1. Tes bisa salah, hanya saja biasanya sepele. Tidak ada gunanya menguji fungsi yang kebenarannya lebih jelas daripada tes. Ini tidak seperti mendapatkan cakupan kode 100% membuat Anda mendekati pengujian setiap kemungkinan eksekusi program, jadi Anda mungkin juga pintar tentang di mana Anda menghabiskan upaya pengujian.
Doval