Unit test metodo privato in c ++ utilizzando una classe friend

13

So che questa è una pratica dibattuta, ma supponiamo che questa sia l'opzione migliore per me. Mi chiedo quale sia la tecnica effettiva per farlo. L'approccio che vedo è questo:

1) Crea una classe di amica con quella della classe con il metodo che voglio testare.

2) Nella classe friend, crea un metodo pubblico che chiama i metodi privati della classe testata.

3) Prova i metodi pubblici della classe friend.

Ecco un semplice esempio per illustrare i passaggi precedenti:

#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;
}

Modifica:

Vedo che nella discussione che segue una delle risposte la gente si interroga sulla mia base di codice.

La mia classe ha metodi che vengono chiamati con altri metodi; nessuno di questi metodi dovrebbe essere chiamato al di fuori della classe, quindi dovrebbero essere privati. Naturalmente potrebbero essere messi in un unico metodo, ma logicamente sono molto meglio separati. Questi metodi sono abbastanza complicati da giustificare il collaudo delle unità e, a causa di problemi di prestazioni, molto probabilmente dovrò ridimensionare questi metodi, quindi sarebbe bello fare un test per assicurarsi che il mio re-factoring non abbia infranto nulla. Non sono l'unico a lavorare nel team, anche se sono l'unico a lavorare su questo progetto, inclusi i test.

Detto questo, la mia domanda non riguardava il fatto che sia una buona pratica scrivere test unitari per metodi privati, anche se apprezzo il feedback.

    
posta Akavall 30.09.2014 - 21:50
fonte

6 risposte

20

Un'alternativa all'amico (beh, in un certo senso) che uso frequentemente è uno schema che ho imparato a conoscere come access_by. È piuttosto semplice:

class A {
  void priv_method(){};
 public:
  template <class T> struct access_by;
  template <class T> friend struct access_by;
}

Ora, supponiamo che la classe B sia coinvolta nel test di A. Puoi scrivere questo:

template <> struct access_by<B> {
  call_priv_method(A & a) {a.priv_method();}
}

Puoi quindi usare questa specializzazione di access_by per chiamare metodi privati di A. Fondamentalmente, ciò che fa è mettere l'onere di dichiarare l'amicizia nel file di intestazione della classe che vuole chiamare i metodi privati di A. Consente inoltre di aggiungere amici ad A senza modificare la fonte di A. Idiomaticamente, indica anche a chi legge la fonte di A che A non indica B un vero amico nel senso di estendere la sua interfaccia. Piuttosto, l'interfaccia di A è completa come data e B ha bisogno di un accesso speciale ad A (test essendo un buon esempio, ho anche usato questo pattern quando implementavo boost python binding, a volte una funzione che deve essere privata in C ++ è utile per esporre nel livello Python per l'implementazione).

    
risposta data 01.10.2014 - 03:04
fonte
9

Se è difficile da testare, è scritto male

Se hai una classe con metodi privati abbastanza complessi da giustificare il proprio test, la classe sta facendo troppo. Dentro c'è un'altra classe che cerca di uscire.

Estrai i metodi privati che vuoi testare in una nuova classe (o classi) e renderli pubblici. Prova le nuove classi.

Oltre a rendere il codice più facile da testare, questo refactoring renderà il codice più facile da capire e da mantenere.

    
risposta data 02.10.2014 - 00:22
fonte
4

Non dovresti testare metodi privati. Periodo. Le classi che usano la tua classe si preoccupano solo dei metodi che fornisce, non di quelli che usa sotto il cofano per funzionare.

Se ti preoccupi della copertura del tuo codice, devi trovare configurazioni che ti consentano di testare il metodo privato da una delle chiamate al metodo pubblico. Se non puoi farlo, qual è il punto di avere il metodo in primo luogo? È semplicemente un codice irraggiungibile.

    
risposta data 30.09.2014 - 21:53
fonte
3

Ci sono alcune opzioni per farlo, ma tieni presente che (in sostanza) modificano l'interfaccia pubblica dei tuoi moduli, per darti accesso ai dettagli di implementazione interna (trasformando efficacemente i test unitari in dipendenze client strettamente collegate, dove dovresti avere tutte le dipendenze no .

  • potresti aggiungere una dichiarazione di amico (classe o funzione) alla classe testata.

  • potresti aggiungere #define private public all'inizio dei file di test, prima di #include -ing il codice testato. Nel caso in cui il codice testato sia una libreria già compilata, questo potrebbe far sì che le intestazioni non corrispondano più al codice binario già compilato (e causa UB).

  • potresti inserire una macro nella classe testata e decidere in una data successiva cosa significhi quella macro (con una definizione diversa per il codice di test). Questo ti permetterebbe di testare internals, ma permetterebbe anche al codice di terze parti di hackerare nella tua classe (creando la propria definizione nella dichiarazione che aggiungi).

risposta data 01.10.2014 - 16:19
fonte
2

Ecco un suggerimento discutibile per una domanda discutibile. Non mi piace l'accoppiamento di un amico come il codice rilasciato deve sapere del test. La risposta di Nir è un modo per alleggerirlo, ma non mi piace ancora cambiare la classe per conformarmi al test.

Dal momento che non mi baso spesso sull'ereditarietà, a volte mi limito a proteggere i metodi privati e ad ereditare ed esporre una classe di test, se necessario. La realtà è che l'API pubblica e l'API di test possono essere diverse e comunque essere distinte dall'API privata, il che ti lascia una specie di vincolo.

Ecco un esempio pratico del tipo su cui ricorro questo trucco. Scrivo codice incorporato e ci affidiamo piuttosto a macchine di stato. L'API esterna non ha necessariamente bisogno di conoscere lo stato della macchina dello stato interno, ma il test dovrebbe (discutibilmente) verificare la conformità al diagramma della macchina dello stato nel documento di progettazione. Potrei esporre un getter "current state" come protetto e quindi dare accesso al test, permettendomi di testare la macchina di stato in modo più completo. Trovo spesso difficile testare questo tipo di classe come una scatola nera.

    
risposta data 02.10.2014 - 03:23
fonte
0

Puoi scrivere il tuo codice con un sacco di soluzioni alternative per impedirti di usare gli amici.

Puoi scrivere classi e non avere mai alcun metodo privato. Tutto quello che devi fare è creare funzioni di implementazione all'interno dell'unità di compilazione, lasciare che la classe li chiami e passare tutti i membri dei dati di cui hanno bisogno per accedere.

E sì vorrà dire che puoi cambiare le firme o aggiungere nuovi metodi di "implementazione" senza cambiare la tua intestazione in futuro.

Devi valutare se vale la pena o meno. E molto dipenderà davvero da chi vedrà la tua intestazione.

Se sto usando una libreria di terze parti preferirei non vedere le dichiarazioni degli amici ai loro tester di unità. Non voglio nemmeno costruire la loro libreria e far eseguire i test quando lo faccio. Sfortunatamente troppe librerie open source di terze parti che ho creato lo fanno.

Il test è il lavoro degli scrittori della biblioteca, non dei suoi utenti.

Tuttavia non tutte le classi sono visibili all'utente della tua biblioteca. Molte classi sono "implementate" e tu le implementa nel modo migliore per assicurarti che funzionino correttamente. In quelli, potresti avere ancora metodi e membri privati, ma vuoi che i tester dell'unità li testino. Quindi vai avanti e fallo in questo modo se ciò porterà a un codice più veloce più veloce, che è facile da mantenere per coloro che hanno bisogno di farlo.

Se gli utenti della tua classe fanno parte della tua azienda o del tuo team, puoi anche rilassarti un po 'di più su questa strategia, supponendo che sia consentita dagli standard di codifica della tua azienda.

    
risposta data 03.10.2014 - 12:28
fonte

Leggi altre domande sui tag