Gli spazi dei nomi anonimi rendono il codice non testabile


13

Ecco un tipico codice C ++:

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;
  ...

Sembra un discreto codice C ++ idiomatico - una classe, una funzione di aiuto matchche viene utilizzata dai metodi di Foo, alcune costanti per quella funzione di aiuto.

E poi voglio scrivere dei test.
Sarebbe perfettamente logico scrivere un test unitario separato match, perché è abbastanza banale.
Ma risiede in uno spazio dei nomi anonimo.
Certo che posso scrivere un test che chiamerebbe Foo::f(). Tuttavia non sarà un buon test se Fooè pesante e complicato, tale test non isolerà il testosterone da altri fattori non correlati.

Quindi devo spostarmi matche tutto il resto fuori dallo spazio dei nomi anonimo.

Domanda: che senso ha mettere funzioni e costanti nello spazio dei nomi anonimo, se li rende inutilizzabili nei test?


3
@ BЈовић rileggi il codice: è presente lo spazio dei nomi anonimo foo.cpp, non l'intestazione! OP sembra capire abbastanza bene che non si dovrebbero mettere spazi dei nomi anon in un'intestazione.
amon,

Qualche idea in Unit Testing Codice C ++ in uno spazio dei nomi senza nome ... ma il "punto" è che l'incapsulamento è buono. Dopotutto è lo stesso problema che hai con le funzioni di membro privato: non possono essere testate in modo non intrusivo, ma non vuoi rinunciare a nascondere le informazioni per il test dell'unità (ad esempio stackoverflow.com/a/3676680/3235496 ).
manlio,

1
Concettualmente il tuo caso non è molto diverso dal caso di testare (o non testare) metodi privati. Quindi, quando cerchi "unit test private" qui su Programmers, otterrai molte risposte che puoi applicare direttamente al tuo caso.
Doc Brown,

@DocBrown Non sto chiedendo come testare le funzioni in un momento. spazi dei nomi. Sto chiedendo "perché mettere il codice in anon. Spazi dei nomi?" (vedi il testo alla fine della domanda). Dai la colpa a Ixrec per aver cambiato il titolo in qualcosa di diverso.
Abyx,

1
@Abyx: in quelle altre risposte che ho menzionato sopra, qui troverai un grande consenso di molti esperti sul fatto che è davvero una cattiva idea testare metodi privati ​​e che friendnon è consigliabile abusare della parola chiave a tale scopo.Combina con il tuo presupposto che se una restrizione per un metodo porta a una situazione in cui non è più possibile testarlo direttamente, ciò implicherebbe che i metodi privati ​​non sono utili.
Doc Brown,

Risposte:


7

Se vuoi testare in unità i dettagli dell'implementazione privata, fai lo stesso tipo di schivata per gli spazi dei nomi senza nome dei membri della classe privati ​​(o protetti):

Fai irruzione e fai festa.

Mentre per le classi che abusate friend, per gli spazi dei nomi senza nome abusate del #includemeccanismo, che non vi costringe nemmeno a cambiare il codice.
Ora che il tuo codice di test (o meglio solo qualcosa per esporre tutto) è nella stessa TU, non c'è problema.

Un avvertimento: se si verificano i dettagli di implementazione, il test si interromperà se questi cambiano. Assicurati davvero di testare solo quei dettagli di implementazione che perderanno comunque, o accetta che il tuo test sia insolitamente effimero.


6

La funzione nel tuo esempio sembra piuttosto complessa e potrebbe essere meglio spostarla nell'intestazione, ai fini del test dell'unità.

che senso ha mettere funzioni e costanti nello spazio dei nomi anonimo, se li rende inutilizzabili nei test?

Per renderli isolati dal resto del mondo. E non sono solo le funzioni e le costanti che puoi inserire nello spazio dei nomi anonimo, ma anche per i tipi.

Tuttavia, se rende i test unitari molto complessi, allora stai sbagliando. In tal caso la funzione non appartiene a questo. Quindi è tempo per un piccolo refactoring di semplificare i test.

Quindi, nello spazio dei nomi anonimo dovrebbero andare solo funzioni molto semplici, a volte costanti e tipi (inclusi i typedef) utilizzati in quell'unità di traduzione.


5

Sarebbe perfettamente logico scrivere un test unitario separato per la corrispondenza, perché è abbastanza non banale.

Il codice che hai mostrato matchè un 1-liner piuttosto banale senza casi limite complicati, o è come un esempio semplificato? Comunque, suppongo che sia semplificato ...

Domanda: che senso ha mettere funzioni e costanti nello spazio dei nomi anonimo, se li rende inutilizzabili nei test?

Questa domanda è ciò che voleva farmi saltare qui da quando Deduplicator ha già mostrato un modo perfettamente valido per irrompere e ottenere l'accesso attraverso l' #includeinganno. Ma la formulazione qui fa sembrare che testare ogni singolo dettaglio di implementazione interna di tutto sia una sorta di obiettivo finale universale, quando è lontano da esso.

L'obiettivo del test unitario uniforme non è sempre quello di testare ogni piccola micro-unità interna granulare di funzionalità. La stessa domanda vale per le funzioni di file-scope statici in C. Si possono anche fare la domanda più difficile da risposta chiedendo il motivo per cui gli sviluppatori utilizzano pimplsin C ++ che richiederebbe sia friendship e #includel'inganno a scatola bianca, negoziazione facile verificabilità dei dettagli di implementazione per il miglioramento dei tempi di compilazione, per esempio

Da una sorta di prospettiva pragmatica, potrebbe sembrare disgustoso ma matchpotrebbe non essere implementato correttamente con alcuni casi limite che lo fanno inciampare. Tuttavia, se l'unica classe esterna Foo, a cui ha accesso, matchnon è possibile utilizzarla in un modo che incontra quei casi limite, allora è piuttosto irrilevante per la correttezza Fooche matchha questi casi limite che non saranno mai incontrati a meno Fooche non cambino, a quel punto i test Foofalliranno e lo sapremo immediatamente.

Una mentalità più ossessiva desiderosa di testare ogni singolo dettaglio dell'implementazione interna (forse un software mission-critical, ad esempio) potrebbe voler entrare e far festa, ma molte persone non pensano necessariamente che sia l'idea migliore, in quanto creerebbe il test più fragili che si possano immaginare. YMMV. Ma volevo solo affrontare la formulazione di questa domanda che fa sembrare che questo tipo di testabilità a livello di dettaglio interno super-fine-grained dovrebbe essere un obiettivo finale, quando anche la mentalità più rigorosa di unit test potrebbe rilassare un po 'qui ed evitare di fare radiografie all'interno di ogni classe.

Quindi perché le persone definiscono le funzioni in spazi dei nomi anonimi in C ++ o come funzioni statiche con ambito di file con collegamento interno in C, nascosto al mondo esterno? E questo è principalmente: nasconderli dal mondo esterno. Ciò ha una serie di effetti dalla riduzione dei tempi di compilazione alla riduzione della complessità (ciò a cui non è possibile accedere altrove non può causare problemi altrove) e così via. Probabilmente la testabilità dei dettagli dell'implementazione privata / interna non è la cosa numero uno nella mente delle persone quando lo fanno, diciamo, riducendo i tempi di costruzione e nascondendo la complessità inutile dal mondo esterno.


Vorrei poter votare questo dieci volte. Essendo una tangente legata al software mission-critical e ad alta affidabilità, sei molto più interessato alla correttezza dei dettagli. Questo stile idiomatico per C ++ non è adatto a questo. Non userei funzionalità del linguaggio come spazi dei nomi anonimi o modelli simili a proxy. Controllerei il mio confine grossolanamente con intestazioni di spazio utente [supportate] e intestazioni di spazio sviluppatore [non supportate].
David,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.