Saya tahu ini adalah praktik yang diperdebatkan, tetapi anggaplah ini adalah pilihan terbaik bagi saya. Saya bertanya-tanya tentang apa teknik sebenarnya untuk melakukan ini. Pendekatan yang saya lihat adalah ini:
1) Buat kelas teman dari kelas yang metode yang ingin saya uji.
2) Di kelas teman, buat metode publik yang memanggil metode pribadi dari kelas yang diuji.
3) Uji metode publik dari kelas teman.
Berikut adalah contoh sederhana untuk menggambarkan langkah-langkah di atas:
#include <iostream>
class MyClass
{
friend class MyFriend; // Step 1
private:
int plus_two(int a)
{
return a + 2;
}
};
class MyFriend
{
public:
MyFriend(MyClass *mc_ptr_1)
{
MyClass *mc_ptr = mc_ptr_1;
}
int plus_two(int a) // Step 2
{
return mc_ptr->plus_two(a);
}
private:
MyClass *mc_ptr;
};
int main()
{
MyClass mc;
MyFriend mf(&mc);
if (mf.plus_two(3) == 5) // Step 3
{
std::cout << "Passed" << std::endl;
}
else
{
std::cout << "Failed " << std::endl;
}
return 0;
}
Edit:
Saya melihat bahwa dalam diskusi mengikuti salah satu jawaban orang bertanya-tanya tentang basis kode saya.
Kelas saya memiliki metode yang dipanggil dengan metode lain; tidak satu pun dari metode ini harus dipanggil di luar kelas, sehingga harus bersifat pribadi. Tentu saja mereka dapat dimasukkan ke dalam satu metode, tetapi secara logis mereka jauh lebih baik terpisah. Metode ini cukup rumit untuk menjamin pengujian unit, dan karena masalah kinerja saya kemungkinan besar harus faktor ulang metode ini, maka akan lebih baik untuk memiliki tes untuk memastikan bahwa re-factoring saya tidak merusak apa pun. Saya bukan satu-satunya yang bekerja di tim, meskipun saya satu-satunya yang mengerjakan proyek ini termasuk tes.
Setelah mengatakan hal di atas, pertanyaan saya bukan tentang apakah itu praktik yang baik untuk menulis unit test untuk metode pribadi, meskipun saya menghargai umpan baliknya.
sumber
Jawaban:
Alternatif untuk teman (dalam arti tertentu) yang sering saya gunakan adalah pola yang saya kenal sebagai access_by. Sederhana saja:
Sekarang, anggaplah kelas B terlibat dalam pengujian A. Anda dapat menulis ini:
Anda kemudian dapat menggunakan spesialisasi access_by ini untuk memanggil metode pribadi A. Pada dasarnya, apa yang dilakukan adalah meletakkan tanggung jawab untuk menyatakan persahabatan ke file header kelas yang ingin memanggil metode pribadi A. Ini juga memungkinkan Anda menambahkan teman ke A tanpa mengubah sumber A. Secara idiomatis, ini juga menunjukkan kepada siapa pun yang membaca sumber A bahwa A tidak menunjukkan B teman sejati dalam arti memperluas antarmuka. Sebaliknya, antarmuka A selesai seperti yang diberikan dan B membutuhkan akses khusus ke A (pengujian menjadi contoh yang baik, saya juga menggunakan pola ini ketika menerapkan boost python binding, terkadang fungsi yang perlu pribadi di C ++ berguna untuk mengekspos ke lapisan python untuk implementasi).
sumber
friend access_by
, bukankah yang pertama bukan cukup teman - menjadi struct bersarang akan memiliki akses ke segala sesuatu dalam A? misalnya. coliru.stacked-crooked.com/a/663dd17ed2acd7a3Jika sulit untuk diuji, itu ditulis dengan buruk
Jika Anda memiliki kelas dengan metode pribadi yang cukup kompleks untuk menjamin tes mereka sendiri, kelas tersebut melakukan terlalu banyak. Di dalam ada kelas lain yang berusaha keluar.
Ekstrak metode pribadi yang ingin Anda uji ke kelas baru (atau kelas) dan buat publik. Uji kelas baru.
Selain membuat kode lebih mudah untuk diuji, refactoring ini akan membuat kode lebih mudah dipahami dan dipelihara.
sumber
Anda seharusnya tidak menguji metode pribadi. Titik. Kelas-kelas yang menggunakan kelas Anda hanya peduli dengan metode yang disediakannya, bukan yang digunakannya di bawah tenda untuk bekerja.
Jika Anda khawatir tentang cakupan kode Anda, Anda perlu menemukan konfigurasi yang memungkinkan Anda untuk menguji metode pribadi dari salah satu panggilan metode publik. Jika Anda tidak bisa melakukan itu, apa gunanya memiliki metode itu? Itu hanya kode yang tidak terjangkau.
sumber
Ada beberapa opsi untuk melakukan ini, tetapi perlu diingat bahwa mereka (pada dasarnya) memodifikasi antarmuka publik dari modul Anda, untuk memberi Anda akses ke detail implementasi internal (secara efektif mengubah pengujian unit menjadi dependensi klien yang sangat erat, di mana Anda harus memiliki tidak ada ketergantungan sama sekali).
Anda bisa menambahkan deklarasi teman (kelas atau fungsi) ke kelas yang diuji.
Anda dapat menambahkan
#define private public
ke awal file pengujian Anda, sebelum#include
membuka kode yang diuji. Jika kode yang diuji adalah pustaka yang sudah dikompilasi, ini bisa membuat header tidak lagi cocok dengan kode biner yang sudah dikompilasi (dan menyebabkan UB).Anda bisa menyisipkan makro di kelas yang diuji dan memutuskan di kemudian hari apa arti makro itu (dengan definisi berbeda untuk kode pengujian). Ini akan memungkinkan Anda untuk menguji internal, tetapi itu juga akan memungkinkan kode klien pihak ketiga untuk meretas ke kelas Anda (dengan membuat definisi sendiri dalam deklarasi yang Anda tambahkan).
sumber
Berikut adalah saran yang dipertanyakan untuk pertanyaan yang dipertanyakan. Saya tidak suka kopling teman karena kode yang dirilis harus tahu tentang tes. Jawaban Nir adalah salah satu cara untuk meringankannya, tetapi saya masih tidak suka mengubah kelas agar sesuai dengan ujian.
Karena saya tidak terlalu bergantung pada pewarisan, saya kadang-kadang hanya membuat metode pribadi yang dilindungi dan memiliki kelas uji mewarisi dan mengekspos sesuai kebutuhan. Kenyataannya adalah bahwa API publik dan API pengujian dapat berbeda dan masih berbeda dari API pribadi, yang membuat Anda terikat.
Berikut adalah contoh praktis dari jenis yang saya gunakan untuk trik ini. Saya menulis kode tertanam, dan kami mengandalkan mesin negara cukup sedikit. API eksternal tidak selalu perlu tahu tentang keadaan mesin keadaan internal, tetapi pengujian harus (bisa dibilang) menguji kesesuaian dengan diagram mesin keadaan dalam dokumen desain. Saya mungkin mengekspos pengambil "keadaan saat ini" sebagai dilindungi dan kemudian memberikan akses tes untuk itu, memungkinkan saya untuk menguji mesin negara lebih lengkap. Saya sering menemukan jenis kelas ini sulit untuk diuji sebagai kotak hitam.
sumber
Anda dapat menulis kode dengan banyak solusi untuk menghentikan Anda harus menggunakan teman.
Anda dapat menulis kelas dan tidak pernah memiliki metode pribadi sama sekali. Yang perlu Anda lakukan adalah membuat fungsi implementasi dalam unit kompilasi, biarkan kelas Anda memanggil mereka dan mengirimkan data anggota yang mereka butuhkan untuk mengakses.
Dan ya itu berarti Anda dapat mengubah tanda tangan atau menambahkan metode "implementasi" baru tanpa mengubah tajuk Anda di masa mendatang.
Anda harus mempertimbangkan apakah layak atau tidak. Dan banyak hal akan sangat tergantung pada siapa yang akan melihat tajuk Anda.
Jika saya menggunakan perpustakaan pihak ke-3 saya lebih suka tidak melihat deklarasi teman untuk penguji unit mereka. Saya juga tidak ingin membangun perpustakaan mereka dan menjalankan tes mereka ketika saya melakukannya. Sayangnya terlalu banyak perpustakaan open source pihak ke-3 yang saya buat melakukan itu.
Pengujian adalah pekerjaan penulis perpustakaan, bukan penggunanya.
Namun tidak semua kelas terlihat oleh pengguna perpustakaan Anda. Banyak kelas adalah "implementasi" dan Anda menerapkannya cara terbaik untuk memastikan mereka berfungsi dengan baik. Pada itu, Anda mungkin masih memiliki metode dan anggota pribadi tetapi ingin penguji unit untuk mengujinya. Jadi lanjutkan dan lakukan dengan cara itu jika itu akan mengarah ke kode yang kuat lebih cepat, yang mudah untuk mempertahankan bagi mereka yang perlu melakukannya.
Jika pengguna kelas Anda semua berada dalam perusahaan atau tim Anda sendiri, Anda juga dapat sedikit lebih santai tentang strategi itu, dengan asumsi itu diizinkan oleh standar pengkodean perusahaan Anda.
sumber