Come utilizzare gli oggetti mock [C ++] senza passarli come argomenti alle funzioni

7

Sono in procinto di integrare un Unit Test Framework per un codice esistente in C ++. Ho azzerato CxxTest, ma come risulta possiamo usare altri Mocking Framework (come googlemock) anche in combinazione con CxxTest.

Dopo aver letto le esercitazioni su CxxTest (Mocking) e googlemock (l'infame esempio di tartaruga), ho l'idea generale che devi definire una classe di simulazione (usando le macro ecc.), dichiarare un oggetto della classe di simulazione e poi passarlo alla funzione sei unità di test. Ora, ci sono molte occorrenze nella base di codice esistente dove non è possibile farlo.

Ecco uno pseudo esempio per chiarire:

class CCandidateForTest
{
    public:
    bool foo(int a)
    {
        CAnotherClass obj;
        int b = obj.bar(a+2);
        if (a == b) {
            return true;
        } else {
            return false;
        }
    }
}

(Questo è troppo semplificato, ci sono anche delle eccezioni e i tipi primitivi possono anche essere altri oggetti ecc., ma si ottiene l'idea generale) (inoltre, la creazione di oggetti non è sempre diretta e può talvolta utilizzare una fabbrica)

Voglio scrivere un test per la funzione CCandidateForTest::foo . Questo metodo crea internamente un oggetto di CAnotherClass , che ho bisogno di simulare in modo che CAnotherClass::bar restituisca valori diversi in modo che percorsi di codice diversi in foo siano attraversati durante il test delle unità.

In poche parole, il problema è che la funzione testata internamente crea un oggetto della classe che deve essere preso in giro, quindi non è possibile passare un'istanza dell'oggetto burlato alla funzione.

Come uso il mocking in questo caso? Esiste un quadro di derisione specifico che rende questo possibile?

    
posta dotbugfix 16.01.2014 - 10:26
fonte

4 risposte

3

Potresti utilizzare una factory per creare istanze di CAnotherClass .

class CCandidateForTest
{
    public:
    bool foo(int a)
    {
        std::shared_ptr< CAnotherClassInterface > obj = CAnotherClassFactory::Create();
        int b = obj->bar(a+2);
        if (a == b) {
            return true;
        } else {
            return false;
        }
    }
}

CAnotherClassFactory può avere il seguente aspetto:

class CAnotherClassFactory
{
public:
    typedef std::shared_ptr< CAnotherClassInterface > (*fn)();
    static std::shared_ptr< CAnotherClassInterface > Create()
    {
        return createFn();
    }

    static fn createFn;
};

Pensieri casuali sul tuo approccio:

  • Se questo è troppo semplificato, allora devi pensare a come rifattarlo.
  • ci sono migliori framework di test unitari quindi cxxunit. gtest e cute-test.com
risposta data 16.01.2014 - 11:26
fonte
4

Utilizza una fabbrica. Chiedi alla classe candidata di entrare nello stabilimento nel costruttore. Prendi in giro la fabbrica per il tuo test. Puoi usare una finta lib per quello o semplicemente usare un programma separato e testare impls di una pura classe factory astratta.

    
risposta data 16.01.2014 - 13:11
fonte
3

Se una classe crea internamente oggetti per fare il suo lavoro, allora quell'oggetto non è affatto un "collaboratore". A scopo di test, questa è esattamente la stessa situazione come se avesse un sacco di codice inline che faceva la stessa cosa. In altre parole, non puoi prendere in giro questo oggetto perché dal punto di vista che il test deve assumere, non esiste!

(Questo potrebbe essere un indicatore della tua classe che fa troppo, e può essere un argomento quando si pesa il refactoring della classe in modo che abbia un vero collaboratore, ma è solo uno fattore - ci possono essere altri buoni motivi per lasciare la classe invariata e testare il suo comportamento nel miglior modo possibile senza deriderlo.)

    
risposta data 16.01.2014 - 10:32
fonte
-2

Ho visto che avevamo una funzione che generava / creava da sé il proprio oggetto da manipolare. Dal mio punto di vista, non è un codice veramente testabile. Pertanto, se provassimo a fare un test unitario, potrebbe non riflettere il vero valore di TDD qui. Inoltre, può rendere il codice di prova dell'unità e oggetti finti più complessi. In modo bilanciato, dovremmo avere un codice refactoring prima di fare qualsiasi UT allora. Possiamo fare in qualche modo un'implementazione del refactoring come: passare più argomenti per renderlo più compatibile con il codice verificabile, usare il modello factory / la funzione globale per generare l'oggetto ..

    
risposta data 11.01.2017 - 10:52
fonte

Leggi altre domande sui tag