Ruang nama anonim membuat kode tidak dapat diuji

13

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 matchyang 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 Fooberat dan rumit, tes seperti itu tidak akan mengisolasi peserta ujian dari faktor-faktor lain yang tidak terkait.

Jadi saya harus pindah matchdan 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?

Abyx
sumber
3
@ BЈовић baca kembali kode - namespace anonim ada foo.cpp, bukan header! OP tampaknya mengerti dengan baik bahwa Anda seharusnya tidak meletakkan spasi nama tunggal di header.
amon
Beberapa ide dalam Unit Testing C ++ Code di dalam Namespace Tanpa Nama ... tetapi "titik" adalah enkapsulasi yang baik. Bagaimanapun, ini adalah masalah yang sama dengan yang Anda miliki dengan fungsi anggota pribadi: mereka tidak dapat diuji secara non-agresif, tetapi Anda tidak ingin menyerahkan informasi yang disembunyikan untuk pengujian unit (mis. Stackoverflow.com/a/3676680/3235496 ).
manlio
1
Kasing Anda secara konseptual tidak jauh berbeda dengan kasing (atau tidak menguji) metode pribadi. Jadi ketika Anda mencari "unit test private" di sini di Programmer, Anda akan mendapatkan banyak jawaban yang dapat Anda terapkan langsung ke kasing Anda.
Doc Brown
@DocBrown Saya tidak bertanya bagaimana cara menguji fungsi di anon. ruang nama. Saya bertanya "mengapa memasukkan kode ke dalam ruang nama saja?" (lihat teks di akhir pertanyaan). Salahkan Ixrec karena mengubah judul menjadi sesuatu yang berbeda.
Abyx
1
@Abyx: dalam jawaban lain yang saya sebutkan di atas, Anda akan menemukan konsensus besar dari banyak ahli di sini bahwa itu adalah ide yang sangat buruk untuk menguji metode pribadi, dan menyalahgunakan friendkata 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.
Doc Brown

Jawaban:

7

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.

Deduplicator
sumber
6

Fungsi dalam contoh Anda terlihat cukup kompleks, dan mungkin lebih baik untuk memindahkannya ke header, untuk tujuan pengujian unit.

apa gunanya menempatkan fungsi dan konstanta ke dalam namespace anonim, jika itu membuat mereka tidak dapat digunakan dalam pengujian?

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.

BЈовић
sumber
5

Sangat logis untuk menulis unit test terpisah untuk pertandingan, karena itu cukup non-sepele.

Kode yang Anda tunjukkan matchadalah 1-liner yang sangat sepele tanpa case edge yang rumit, atau apakah itu seperti contoh sederhana? Bagaimanapun, saya akan menganggap itu disederhanakan ...

Pertanyaan: apa gunanya menempatkan fungsi dan konstanta ke dalam namespace anonim, jika itu membuat mereka tidak dapat digunakan dalam pengujian?

Pertanyaan ini yang ingin membuat saya melompat ke sini karena Deduplicator sudah menunjukkan cara yang sangat baik untuk masuk dan mendapatkan akses melalui #includetipu 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 pimplsC ++ yang membutuhkan keduanya friendship dan #includetipu daya ke kotak putih, memperdagangkan testabilitas yang mudah dari detail implementasi untuk meningkatkan waktu kompilasi, misalnya

Dari semacam perspektif pragmatis, mungkin terdengar kotor tetapi matchmungkin tidak diimplementasikan dengan benar dengan beberapa kasus tepi yang menyebabkannya naik. Namun, jika satu-satunya kelas luar Foo,, yang memiliki akses ke matchtidak mungkin menggunakannya dengan cara yang bertemu dengan kasus tepi tersebut, maka itu agak tidak relevan dengan kebenaran Fooyang matchmemiliki kasus tepi ini yang tidak akan pernah ditemukan kecuali Fooperubahan, pada titik mana tes Fooakan 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
Saya berharap saya bisa memperbaiki ini sepuluh kali. Sebagai garis singgung terkait dengan misi kritis, perangkat lunak jaminan tinggi, Anda jauh lebih mementingkan kebenaran rincian. Gaya idiomatis untuk C ++ ini tidak cocok untuk itu. Saya tidak akan menggunakan fitur bahasa seperti ruang nama anonim atau pola seperti proxy. Saya akan mengontrol batas saya secara kasar dengan header ruang pengguna [didukung] dan header ruang pengembang [tidak didukung].
David