Domanda

Attualmente sto lavorando su una classe che utilizza un'altra classe, che ha solo funzioni statiche.

Tutto ha funzionato bene fino a quando ho provato a testare la mia classe.

Ecco un semplice esempio di codice del problema:

class A {
    static String getSometing() {
        return String("Something: ") + heavyCalculation().asString();
    }
}

class B {
    B() {}
    ~B() {}
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return A::getSomething();
        } else {
            return "Invalid name!";
        }
    }
}

Supponendo che la classe A è funziona correttamente (ed è stato testato dai suoi test di unità), vorrei controllare il runSomething funzione in classe B.

La mia prima opzione sarebbe la creazione di simulazioni per le classi interne (In questo esempio in classe A), ma in questo caso non mi darà nulla da ereditare da Una perché ha solo funzioni statiche.

La mia seconda opzione è quella di incapsulare le chiamate per Una classe di funzioni private all'interno di B, così ho potuto controllare i loro valori di ritorno (anche se la scelta di questa opzione farà bene un po ' più complesso).

La mia domanda a voi è: Ci sono nel migliore dei modi per testare le classi in C++ che dipende statico classi/funzioni rispetto alla mia attuale le opzioni?

Grazie in anticipo,

Tal.

È stato utile?

Soluzione

Ho avuto successo in Unit Testing in situazioni simili refactoring i riferimenti alle funzioni statiche (nel mio caso è stato dipendenze esterne) in nuove funzioni private e sovrascrivendo sulla matrice testato, in modo da poter consigliare questo approccio

Se le funzioni di refactoring rimangono riservate, che non dovrebbe essere di grande impatto sulla complessità del progetto e dovrebbero essere abbastanza piccolo da non avere un impatto negativo sulla leggibilità del codice.

Altri suggerimenti

Se non si utilizza una suite di test monolitico, allora la sua facile. Suppongo di avere classe A in A.cpp e la classe B in B.cpp, ed i test per B sono in B_test.cpp.

Creare un file chiamato A_mock.cpp

class A
{
    static String getSometing() {
        return String("Expected Something");
    }
};

Poi, quando si compila il file B_test, proprio legame con A_mock.o piuttosto che A. Ó.

g++ -Wall B_test.cpp B.cpp A_mock.cpp

Si potrebbe passare un puntatore alla funzione al costruttore della classe A. Quindi per testare, è possibile passare un po 'il puntatore a una funzione finto dove si può fare quello che vuoi.

Perché la funzione statica? Vorrei suggerire non farcela statica.

Si potrebbe quindi creare un'interfaccia per la classe A (in C ++ questo mezzo una classe con solo puri intestazioni funzione virtuale) di nome AInterface. Classe A attuerebbe (ereditano) AInterface e implementare questo funzioni virtuali.

Poi passare un puntatore a questa interfaccia nel costruttore della classe di B e conservarla in una variabile membro denominata m_A. Poi, nel tuo test, creare MockClassA che implementa AInterface. Passo MockClassA in costruttore di classe B e impostare m_A all'ingresso.

class AInterface
{
   virtual String getSomething() = 0;
}

class A : public AInterface
{
    String getSometing() {
        return String("Something: ") + heavyCalculation().asString();
    }
}

class B 
{
    B(AInterface A) :  { m_A = A; }
    ~B() {}
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return m_A.getSomething();
        } else {
            return "Invalid name!";
        }
    }
    AInterface m_A;
}

Codice di prova

class MockClassA : public AInterface
{
    String getSometing() {
        return String("Whatever I want. This is a test");
    }
}   

void test ()
{
   // "MockClassA" would just be "A" in regular code
   auto instanceOfB = B(MockClassA());

   String returnValue = instanceOfB.runSomething("something");
   :
   :
}

direi, "L'uomo, alcune persone prendono Unit Testing troppo lontano!"

Basta testare le due classi come una singola unità. La Classe A è difficile codificato in classe B in ogni caso.

Si dovrebbe prendere la classe, tramite il modello, e esportazione in modo esplicito che la creazione di istanze (B<A>) per evitare linker problemi, se non fosse in precedenza, tutti in linea come inviato.In questo modo, è possibile inserire altre classi per le prove di cui hai bisogno, ed è una buona pratica in ogni caso.Sono anche curiosa di sapere perché il tuo esempio si presenta in modo molto simile a Java - ho dovuto leggerlo circa cinque volte prima di determinare che è in realtà era C++ come indicato.

template<typename T> class BImpl {
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return T::getSomething();
        } else {
            return "Invalid name!";
        }
    }
};
typedef BImpl<A> B; // Just plugs in to existing code.

Ora è possibile sostituire un finto classe, anche se non è possibile ereditare da essa.Infatti, questo è ANCHE allungabile in un altro modo - CRTP.

class A : public BImpl<A> {
    String getSomething() {
        // Now it's non-static! IT'S A MIRACLE!
    }
}

Le meraviglie di modelli, mai e poi mai di stupirmi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top