Berikut ini adalah kode C ++ yang khas:
foo.hpp
#pragma once
class Foo {
public:
void f();
void g();
...
};
foo.cpp
#include "foo.hpp"
namespace {
const int kUpperX = 111;
const int kAlternativeX = 222;
bool match(int x) {
return x < kUpperX || x == kAlternativeX;
}
} // namespace
void Foo::f() {
...
if (match(x)) return;
...
Itu terlihat seperti kode C ++ idiomatis yang layak - sebuah kelas, fungsi helper match
yang digunakan oleh metode Foo
, beberapa konstanta untuk fungsi helper itu.
Dan kemudian saya ingin menulis tes.
Akan sangat logis untuk menulis unit test terpisah match
, karena itu cukup non-sepele.
Tapi itu berada di namespace anonim.
Tentu saja saya bisa menulis tes yang akan menelepon Foo::f()
. Namun itu tidak akan menjadi tes yang baik jika Foo
berat dan rumit, tes seperti itu tidak akan mengisolasi peserta ujian dari faktor-faktor lain yang tidak terkait.
Jadi saya harus pindah match
dan yang lainnya keluar dari namespace anonim.
Pertanyaan: apa gunanya menempatkan fungsi dan konstanta ke dalam namespace anonim, jika itu membuat mereka tidak dapat digunakan dalam pengujian?
sumber
foo.cpp
, bukan header! OP tampaknya mengerti dengan baik bahwa Anda seharusnya tidak meletakkan spasi nama tunggal di header.friend
kata kunci untuk tujuan itu tidak dianjurkan. Gabungkan dengan asumsi Anda bahwa jika pembatasan untuk suatu metode mengarah ke situasi di mana Anda tidak dapat mengujinya secara langsung lagi, itu akan menyiratkan metode pribadi tidak berguna.Jawaban:
Jika Anda ingin menguji unit-detail implementasi privat, Anda melakukan penghindaran yang sama untuk ruang nama yang tidak disebutkan namanya seperti untuk anggota kelas pribadi (atau dilindungi):
Istirahat dan pesta.
Sedangkan untuk kelas Anda menyalahgunakan
friend
, untuk ruang nama yang tidak disebutkan namanya Anda menyalahgunakan#include
-mekanisme, yang bahkan tidak memaksa Anda untuk mengubah kode.Sekarang kode tes Anda (atau lebih baik hanya sesuatu untuk mengekspos semuanya) berada di TU yang sama, tidak ada masalah.
Peringatan: Jika Anda menguji detail implementasi, pengujian Anda akan pecah jika itu berubah. Pastikan untuk hanya menguji rincian implementasi tersebut yang akan bocor, atau menerima bahwa tes Anda sangat singkat.
sumber
Fungsi dalam contoh Anda terlihat cukup kompleks, dan mungkin lebih baik untuk memindahkannya ke header, untuk tujuan pengujian unit.
Untuk membuat mereka terisolasi dari dunia. Dan bukan hanya fungsi dan konstanta yang dapat Anda masukkan ke dalam namespace anonim - tetapi juga untuk tipe.
Namun, jika itu membuat unit test Anda sangat kompleks, maka Anda salah melakukannya. Dalam kasus seperti itu, fungsinya tidak termasuk di sana. Maka sudah waktunya untuk sedikit refactoring untuk membuat pengujian lebih sederhana.
Jadi, dalam namespace anonim harus hanya fungsi yang sangat sederhana, terkadang konstanta dan tipe (termasuk typedef) yang digunakan dalam unit terjemahan itu.
sumber
Kode yang Anda tunjukkan
match
adalah 1-liner yang sangat sepele tanpa case edge yang rumit, atau apakah itu seperti contoh sederhana? Bagaimanapun, saya akan menganggap itu disederhanakan ...Pertanyaan ini yang ingin membuat saya melompat ke sini karena Deduplicator sudah menunjukkan cara yang sangat baik untuk masuk dan mendapatkan akses melalui
#include
tipu daya. Tetapi kata-kata di sini membuatnya seperti menguji setiap detail implementasi internal tunggal dari segala sesuatu adalah semacam tujuan akhir universal, ketika itu jauh dari itu.Tujuan pengujian satuan sekalipun tidak selalu menguji setiap unit mikro internal granular kecil fungsionalitas. Pertanyaan yang sama berlaku untuk fungsi ruang lingkup file statis di C. Anda bahkan dapat membuat pertanyaan lebih sulit untuk dijawab dengan menanyakan mengapa pengembang menggunakan
pimpls
C ++ yang membutuhkan keduanyafriendship
dan#include
tipu daya ke kotak putih, memperdagangkan testabilitas yang mudah dari detail implementasi untuk meningkatkan waktu kompilasi, misalnyaDari semacam perspektif pragmatis, mungkin terdengar kotor tetapi
match
mungkin tidak diimplementasikan dengan benar dengan beberapa kasus tepi yang menyebabkannya naik. Namun, jika satu-satunya kelas luarFoo
,, yang memiliki akses kematch
tidak mungkin menggunakannya dengan cara yang bertemu dengan kasus tepi tersebut, maka itu agak tidak relevan dengan kebenaranFoo
yangmatch
memiliki kasus tepi ini yang tidak akan pernah ditemukan kecualiFoo
perubahan, pada titik mana tesFoo
akan gagal dan kami akan segera tahu.Pola pikir yang lebih obsesif ingin menguji setiap detail implementasi internal (mungkin perangkat lunak mission-critical, misalnya) mungkin ingin masuk dan berpesta, tetapi banyak orang tidak selalu berpikir itu adalah ide terbaik, karena akan menciptakan kebanyakan tes rapuh bisa dibayangkan. YMMV. Tetapi saya hanya ingin membahas kata-kata dari pertanyaan ini yang membuatnya terdengar seperti jenis testabilitas level-uber-fine-grained-internal-detail-harus menjadi tujuan akhir, ketika bahkan pola pikir unit testing yang paling keras mungkin sedikit santai di sini dan menghindari x-raying internal setiap kelas.
Jadi mengapa orang mendefinisikan fungsi dalam ruang nama anonim di C ++ atau sebagai fungsi statis ruang-file dengan tautan internal di C, disembunyikan dari dunia luar? Dan terutama itu: untuk menyembunyikan mereka dari dunia luar. Itu memiliki sejumlah efek dari mengurangi waktu kompilasi hingga mengurangi kompleksitas (apa yang tidak dapat diakses di tempat lain tidak dapat menyebabkan masalah di tempat lain) dan sebagainya. Mungkin testability dari detail implementasi pribadi / internal bukanlah hal utama dalam pikiran orang ketika mereka melakukannya, katakanlah, mengurangi waktu pembangunan dan menyembunyikan kompleksitas yang tidak perlu dari dunia luar.
sumber