2015-06-18 12 views
19

Recentemente ho scoperto gmock e sono ora in procinto di "ripensare l'intero processo di programmazione così com'è", aggiungendo i test unitari ovunque io sia possibile. Una cosa che mi è sembrata strana nel processo è che il modulo QSql è chiaramente una dipendenza esterna dal nostro codice, non offre agli sviluppatori strumenti per deridere i suoi interni. La cosa migliore che posso pensare con questo modulo è il DB in memoria, che è molto più difficile da implementare di un semplice mock e non è nemmeno sempre possibile (considerare la possibilità di creare pacchetti oracle con DB in memoria)C'è un modo per deridere QSqlQuery?

Ora, per me , non è esattamente un problema, un po 'di tempo fa siamo passati al wrapper ocilib home grown che eredita dall'interfaccia virtuale (ed è, quindi, facilmente raggibile). Ma davvero, non c'è modo di deridere quando usi il modulo QSql di Qt? O meglio: se si tratta di un framework (veramente buono), non forniscono veramente automazione per casi di questo tipo o mi manca qualcosa?

UPD1: Un piccolo aggiornamento sulla importanza della questione:

mio codice è molto pesantemente interleaved con le query SQL di Oracle come, per alcuni, il codice di un sacco di altre persone. L'unità che verifica un tale codice è praticamente impossibile quando una dipendenza esterna, anch'essa molto sviluppata, a volte fornisce dati non corretti. Quando il tuo test unitario si interrompe, vuoi che sia il tuo codice che è in errore, non Oracle. Ecco perché ho fatto la domanda originale. Se esiste/esiste un modo per simulare facilmente la dipendenza usando l'interfaccia qsqlquery, allora è possibile scrivere test unitari per il codice usando QSql.

UPD2: Anche se, dopo un'ulteriore considerazione, devo ammettere che il problema potrebbe essere evitato con una migliore progettazione del codice (OO invece di funzioni libere in alcuni punti) e una migliore separazione delle entità. Quindi, praticamente impossibile in UPD1 non era veramente giustificato. Anche se questo non rende la domanda originale meno importante. Quando hai il compito di mantenere il codice legacy, ad esempio, il mocking QtSql è l'unico modo realistico di introdurre test sul sistema.

+1

È possibile provare a implementare il proprio driver sql falso, in cui è possibile implementare le funzioni di verifica. – Milovidov

+0

beh, non è molto facile da usare, non è così? :) Mi aspetterei che un driver falso sia già pronto per il riutilizzo di ppl – Zeks

+0

Cosa c'è di peggio, se vuoi simulare un driver dovrai inevitabilmente imparare esattamente , quando e come viene chiamato da qsqlquery, perché altrimenti non sarai in grado di falsificare correttamente le chiamate ad esso – Zeks

risposta

1

Se si desidera solo un database SQL in memoria da utilizzare come back-play fittizio per QtSQL, è possibile considerare l'utilizzo di SQLite.

SQLite è una libreria in-process che implementa un motore di database SQL transazionale self-contained, senza server, con configurazione zero. Il codice per SQLite è di dominio pubblico ed è quindi gratuito per l'uso per qualsiasi scopo, commerciale o privato. SQLite è il database più diffuso al mondo con più applicazioni di quelle che possiamo contare, inclusi numerosi progetti di alto profilo.

Il vantaggio di utilizzare un vero interprete SQL dietro le chiamate QtSQL è che è possibile convalidare la sintassi SQL inoltrata e se la query restituisce effettivamente il risultato previsto.

Se la tua preoccupazione è testare query SQL che esercitano funzionalità specifiche di Oracle SQL, non c'è altro modo per sapere che stai utilizzando queste funzionalità correttamente senza testare un vero server Oracle SQL.

+0

Non voglio testare le funzionalità specifiche di Oracle. Quello che stai descrivendo è un falso, non un finto. Voglio testare le mie funzioni senza alcun tipo di finto plug-in, con oggetti mock molto più semplici che mi permettono di non fare l'evento su ciò che sto collegando a – Zeks

+0

Indipendentemente da ciò, non vuoi prendere in giro una risposta corretta a una query SQL errata, quindi è meglio usare un vero interprete SQL. – jxh

+0

Non voglio prendere in giro una risposta a una query errata. Voglio prendere in giro una query in modo che sia sicuro che il codice circostante funzioni come previsto. è uno scopo completamente diverso – Zeks

1

Zeks, IMO si hanno 2 approcci per deridere le classi Qt SQL:

classi
  1. Subclassing Qt Sql;
  2. Wrapper attorno alle classi Qt Sql e passaggio attraverso le interfacce.

Approccio # 1:

In generale si tratta di dolore. Innanzitutto, se si desidera simulare QSqlQuery, è necessario creare sottoclassi per QSqlResult, QSqlDriver e QSqlQuery stesso. Poi, un altro dolore entra in gioco, è necessario impostare precondizioni - ad esempio: volete che il vostro SQL per restituire vera a chiamare la funzione exec(), per questo scopo, la sottoclasse di QSqlDriver deve tornare:

class QSqlDriverTest : public QSqlDriver 
{ 
    ... 
    virtual bool isOpen() const { return true; } 
    virtual void setOpenError(bool e) { QSqlDriver::setOpenError(false); } 
    ... 
}; 

Questo è solo un esempio. Ci sono ancora più precondizioni per chiamare con successo la funzione next(). Per scoprirli è sempre necessario consultare il codice sorgente qt. Quindi dipende interamente da qt. Questo approccio non riesce perché:

  • non è facile - il test dell'unità deve essere semplice;
  • hai ancora dipendenza qt;

Approccio # 2:

Penso che sia il modo migliore e chiaro per deridere le query, ma è necessario preparare il vostro codice. Si crea un'interfaccia ISQLQuery che ha le stesse funzioni di QSqlQuery (lo stesso per QSqlDriver e QSqlResult). Come questo:

class ISQLQuery // interface wrapper for QSqlQuery 
{ 
public: 
    ~ISQLQuery(){} 
    ... 
    virtual bool exec() = 0; 
    virtual QVariant value(int i) const = 0; 
    virtual const ISQLDriver * driver() const = 0; 
    ... 
}; 

class ISQLDriver // interface wrapper for QSqlDriver 
{ 
public: 
    ~ISQLDriver(){} 
    ... 
    virtual bool subscribeToNotification(const QString & name) = 0; 
    ... 
}; 

quindi si crea implementazioni reali (questo è solo progetto idea):

class SQLDriver : public ISQLDriver 
{ 
public: 
    SQLDriver(const QSqlDriver * driver) : mpDriver(driver){} 
    ... 
    virtual bool subscribeToNotification(const QString & name) 
     { return mpDriver->subscribeToNotification(name); } 
    ... 
private: 
    const QSqlDriver * mpDriver; 
}; 

class SQLQuery : public ISQLQuery 
{ 
public: 
    SQLQuery(): mDriver(mQuery->driver){} 
    ... 
    virtual bool exec() { return mQuery.exec(); } 
    virtual QVariant value(int i) const { return mQuery.value(i); } 
    virtual const SQLDriver * driver() const { return &mDriver; } 
    ... 
private: 
    QSqlQuery mQuery; 
    SQLDriver mDriver; 
    ... 
}; 

C'è un esempio di come utilizzare le nuove classi SQL, quando tutte le interfacce sono creati e implementati:

// some function 
{ 
    ... 
    SQLQuery query = SQLFactory::createSQLQuery(); // here you can put your mocks 
    query.prepare("DROP TABLE table_hell;"); 
    query.exec(); 
    ... 
} 

Ti ho mostrato l'idea principale senza tutti i dettagli altrimenti il ​​post potrebbe diventare enorme. Spero che troverai utile la mia spiegazione.

Saluti.