2010-08-10 12 views
13

Ho una domanda sul test delle query in una transazione. Sto usando le transazioni MySQL per un bel po 'di tempo, e ogni volta che lo faccio, io uso qualcosa di simile:Come testare le transazioni MySQL?

$doCommit = true; 
$error = ""; 
mysql_query("BEGIN"); 

/* repeat this part with the different queries in the transaction 
    this often involves updating of and inserting in multiple tables */ 
$query = "SELECT, UPDATE, INSERT, etc"; 
$result = mysql_query($query); 
if(!$result){ 
    $error .= mysql_error() . " in " . $query . "<BR>"; 
    $doCommit = false; 
} 
/* end of repeating part */ 

if($doCommit){ 
    mysql_query("COMMIT"); 
} else { 
    echo $error; 
    mysql_query("ROLLBACK"); 
} 

Ora, accade spesso che voglio provare la mia transazione, così ho cambiare mysql_query("COMMIT");-mysql_query("ROLLBACK"); , ma posso immaginare che questo non è un ottimo modo per testare questo tipo di cose. Di solito non è davvero fattibile copiare ogni tabella in un temp_table e aggiornarla e inserirla in quelle tabelle ed eliminarle successivamente (ad esempio perché le tabelle possono essere molto grandi). Ovviamente, quando il codice entra in produzione, viene gestita la relativa gestione degli errori (anziché solo la stampa dell'errore).

Qual è il modo migliore per fare cose del genere?

+0

Oh caro ... Ho appena scoperto che PHP non supporta elementi come 'finally' o' ensure'. –

+2

Sì, lo so ... PHP fa schifo e io lo uso ... –

+0

Ora è disponibile il supporto per 'finally', da PHP 5.5 – Alex

risposta

10

Prima di tutto, c'è un bug nella tua implementazione. In caso di errore di una query, la transazione corrente viene automaticamente ripristinata e quindi chiusa. Quindi, mentre continui ad eseguire le query, non saranno all'interno di una transazione (saranno comunque commesse nel DB). Quindi, quando esegui Rollback, fallirà automaticamente. Dal MySQL docs:

Rolling back can be a slow operation that may occur implicitly without the user 
having explicitly asked for it (for example, when an error occurs). 

Il comando esplicito ROLLBACK deve essere utilizzato solo se si determina nell'applicazione che è necessario eseguire il rollback (per motivi diversi di un errore di query). Ad esempio, se stai detrando fondi da un account, eseguirai esplicitamente il rollback se scoprirai che l'utente non ha fondi sufficienti per completare lo scambio ...

Per quanto riguarda il test delle transazioni, lo faccio copia il database. Creo un nuovo database e installo una serie di "dati fittizi". Quindi eseguo tutti i test utilizzando uno strumento automatico. Lo strumento eseguirà effettivamente il commit delle transazioni e imporrà il rollback e verificherà che lo stato del database previsto venga mantenuto durante i test. Dal momento che è più difficile conoscere a livello programmatico lo stato finale di una transazione se si dispone di un input sconosciuto alla transazione, testare i dati dal vivo (o anche copiati dal vivo) non sarà facile. Puoi farlo (e dovrebbe), ma non dipendere da questi risultati per determinare se il tuo sistema funziona. Utilizza questi risultati per creare nuovi casi di test per il tester automatico ...

+0

Wow, non ho ancora fatto i miei compiti su questo punto ... Davvero non sapevo che MySQL tornasse indietro quando si verifica un errore. Grazie per l'intuizione. Grazie per il resto, questo è un post davvero utile. –

+0

Sicuro. Vedo questo errore abbastanza spesso (dato che è contro-intuitivo se non lo si aspetta) ... È anche un problema piuttosto difficile eseguire il debug quando si sta esaminando un log di esecuzione cercando di capire perché alcune query commesso anche se il comando rollback è stato eseguito ... Ecco perché lancio sempre eccezioni sugli errori di query (Soprattutto se si considera che il codice di produzione non dovrebbe mai errore a meno che qualcosa non sia andato veramente male (come il server è andato via). non sai cosa è andato storto?) ... È molto più difficile ignorarlo in questo modo ... – ircmaxell

+0

Hai ragione. Nel tuo post parli di un tester automatico, l'hai fatto tu stesso? Posso immaginare che diversi tester sono necessari per diversi progetti (e forse anche per diversi casi di test). –

2

genere io uso qualcosa di simile (io uso DOP per il mio esempio):

$db->beginTransaction(); 
try { 
    $db->exec('INSERT/DELETE/UPDATE'); 
    $db->commit(); 
} 
catch (PDOException $e) { 
    $db->rollBack(); 
    // rethrow the error or 
} 

Oppure, se avete il vostro gestore di eccezioni, utilizzare una clausola speciale per i vostri PDOExceptions, dove poter far ritirare l'esecuzione. Esempio:

function my_exception_handler($exception) { 
    if($exception instanceof PDOException) { 
    // assuming you have a registry class 
    Registry::get('database')->rollBack(); 
    } 
} 
+0

Sì, ma come si verifica la transazione? Il punto della mia domanda è che voglio sapere come testare tutte le query in un'unica transazione. Nel tuo caso sostituirò '$ db-> commit();' con '$ db-> rollBack();', ma sarebbe lo stesso che avevo nella mia domanda originale. –

3

Forse potresti rifattorizzare il tuo primo esempio e utilizzare qualche classe di wrapper access DB?

In tale classe wrapper è possibile avere una variabile $ normalCommit = true; e un metodo SetCommitMode() che imposta quella variabile $ normalCommit. E si ha un metodo Commit() che commette if ($ normalCommit == true) Oppure ha anche una variabile $ failTransaction che chiama mysql_query ("ROLLBACK"); se lo desideri (così potresti passare/fallire molti test sequenziali).

Quindi, quando si esegue il test, è possibile impostare da qualche parte nel file del codice di test: $ myDBClass-> SetCommitMode (false); o $ myDBClass-> RollBackNextOperation (true); prima dell'operazione che si desidera fallire, e fallirà.In tal modo il codice che si sta testando non conterrà i controlli fail/commit, solo la classe DB li conterrà.

e normalmente Onlly il codice di prova (soprattutto se si fa il test delle unità) dovrebbe chiamare quelli SetCommitMode e metodi RollBackNextOperation, in modo da non lasciare accidentalmente quelle chiamate nel codice di produzione.

Oppure potresti passare alcuni dati pazzi al tuo metodo (se stai testando un metodo), come le variabili negative da salvare nei campi UNSIGNED, e quindi la transazione dovrebbe fallire al 100% se il tuo codice non esegue il commit dopo tale Errore SQL (ma non dovrebbe).

+0

Grazie per la risposta, è una soluzione sensata. Se potessi dividere la taglia, avresti ottenuto il 50% ... –