Ciao a tutti,
sono alla continua ricerca del miglior modo di scrivere codice con approccio TDD utilizzando il framework Yii. Ad oggi la maggior parte delle applicazioni web è composta da un frontend, uno strato di API (solitamente JSON) per comunicare in modo asincrono con il server ed un backend.
I livelli di test che uso su applicazioni di questo tipo sono prevalentemente quelli unitari per i modelli e quelli funzionali per l’interfaccia. Per questi ultimi l’approccio più “pubblicizzato” su guide, demo e manuali è quello di usare PHPUnit + Selenium ma anche Behat + Mink sembrano essere una buona accoppiata (che devo ancora provare seriamente).
Se avete confidenza con i test funzionali che utilizzano un oggetto browser (come Selenium) saprete meglio di me che meno avete bisogno di farli girare e meglio vi sentite . Questo perchè i tempi di caricamento sono molto più lunghi, il codice dei test è meno manutenibile e alcuni casi d’uso (ad es. Il processo di registrazion/Login con l’ SDK js di Facebook che usa il popup) sono poco piacevoli da scrivere.
Se l’applicazione è molto vicina all’essere una single web page application (e quindi ad avere molte chiamate asincrone) mi pongo il problema di dover testare i corretti output JSON delle chiamate API. Vorrei testare questo tipo di chiamate sotto forma di test unitario per renderli più veloci e manutenibili. Considerando che molte di queste chiamate sono permesse solo ad utenti loggati tramite il filtro accessControl ci ho pensato un po su e dall’idea che mi sono fatto fin’ora le possibilità sono essenzialmente 2:
[list=1]
[*] usare cUrl verso l’endpoint che restituisce il JSON
[*] fare una chiamata diretta alla funzione del controller
[/list]
Nel primo caso si possono applicare le fixture ma non è in alcun modo possibile mockare il CWebUser perchè se si usa Apache la chiamata fatta da cUrl avvia una nuova istanza dell’applicazione che non è quella avviata dai test. Questo è risolvibile rendendo tutte le API restless (attualmente lavoro non da poco) e quindi evitando il problema di usare l’accessControl per filtrare l’accesso alla funzione in questione.
Nel secondo l’unico modo che ho trovato di mockare il webuser è quello di ridefinirlo nella classe del test che sta girando. Questa cosa sembra funzionare ma se nella mia classe di test ho dei casi d’uso che richiedono diversi tipi di utente ho il problema che non posso in alcun modo cambiare a runtime (e nè dinamicamente al momento del setup) il mock del webuser. Per altro l’unico modo che ho trovato di mockare il webuser è quello che trovate sotto, perchè $this->getMock(‘WebUser’) non influisce in alcun modo sul singleton WebUser definito nella configurazione di CWebApplication.
Vi porto un esempio concreto, actionAjaxGetFavStore() può essere chiamata solo se l’utente è loggato:
class UserControllerTest extends CDbTestCase
{
public $fixtures=array(
/* NEEDED FIXTURES*/
);
public function testUserCanGetFavouriteStore() {
$controller = new UserController(1);
$result = json_decode($controller->actionAjaxGetFavStore());
$this->assertInternalType('array', $result->data);
$model = $result->data[0];
$this->assertEquals($model->name, "Nome dello Store");
}
}
class WebUser extends CWebUser {
public function getId() {
return 1;
}
public function getIsGuest() {
return false;
}
};
Conoscete una strada migliore? C’è qualcosa di sbagliato a livello concettuale nel mio caso?