Posso forse darti un assaggio della nostra esperienza quando abbiamo iniziato a esaminare i test unitari del nostro processo di livello intermedio che includeva una tonnellata di operazioni sql "business logic".
Per prima cosa abbiamo creato un livello di astrazione che ci ha permesso di "inserire" qualsiasi connessione di database ragionevole (nel nostro caso, abbiamo semplicemente supportato una singola connessione di tipo ODBC).
Una volta che questo è stato messo in atto, siamo stati in grado di fare qualcosa del genere nel nostro codice (lavoriamo in C ++, ma sono sicuro che avrai l'idea):
GetDatabase (). ExecuteSQL ("INSERT INTO foo (blah, blah)")
In fase di esecuzione normale, GetDatabase () restituiva un oggetto che alimentava tutti i nostri sql (comprese le query), tramite ODBC direttamente al database.
Abbiamo quindi iniziato a esaminare i database in memoria: il migliore di gran lunga sembra essere SQLite. ( http://www.sqlite.org/index.html ). È straordinariamente semplice da configurare e utilizzare e ci ha consentito di eseguire la sottoclasse e sovrascrivere GetDatabase () per inoltrare sql a un database in memoria creato e distrutto per ogni test eseguito.
Siamo ancora nelle prime fasi di questo, ma finora sembra buono, tuttavia dobbiamo assicurarci di creare tutte le tabelle necessarie e popolarle con i dati di test - tuttavia abbiamo ridotto il carico di lavoro qui creando un insieme generico di funzioni di supporto che può fare molto di tutto questo per noi.
Nel complesso, ha aiutato immensamente il nostro processo TDD, dal momento che apportare modifiche apparentemente innocue per correggere alcuni bug può avere effetti piuttosto strani su altre aree (difficili da rilevare) del tuo sistema - a causa della natura stessa di sql / database.
Ovviamente, le nostre esperienze sono state incentrate su un ambiente di sviluppo C ++, tuttavia sono sicuro che potresti riuscire a far funzionare qualcosa di simile in PHP / Python.
Spero che questo ti aiuti.