Différentes méthodes de test
Définissez d’abord ce que vous faites: Test unitaire ou test d’intégration . Le nombre de couches n'est pas pertinent pour les tests unitaires car vous ne testez qu'une seule classe. Vous vous moquez du reste. Pour les tests d'intégration, il est inévitable de tester plusieurs couches. Si vous avez de bons tests unitaires en place, l’astuce consiste à rendre les tests d’intégration peu complexes.
Si vos tests unitaires sont bons, vous n'avez pas à répéter tous les détails lors des tests d'intégration.
Les termes que nous utilisons sont un peu dépendants de la plate-forme, mais vous pouvez les trouver dans presque toutes les plates-formes de test / développement:
Exemple d'application
En fonction de la technologie utilisée, les noms peuvent différer, mais je vais utiliser ceci à titre d'exemple:
Si vous avez une application CRUD simple avec le modèle Product, ProductsController et une vue d'index qui génère un tableau HTML avec les produits:
Le résultat final de l'application montre un tableau HTML avec une liste de tous les produits actifs.
Tests unitaires
Modèle
Le modèle que vous pouvez tester assez facilement. Il existe différentes méthodes pour cela; nous utilisons des fixtures. Je pense que c'est ce que vous appelez des "faux jeux de données". Ainsi, avant que chaque test soit exécuté, nous créons la table et intégrons les données d'origine. La plupart des plates-formes ont des méthodes pour cela. Par exemple, dans votre classe de test, une méthode setUp () qui est exécutée avant chaque test.
Ensuite, nous exécutons notre test, par exemple: les produits testGetAllActive .
Nous testons donc directement sur une base de données de test. Nous ne simulons pas la source de données; nous faisons toujours la même chose. Cela nous permet par exemple de tester avec une nouvelle version de la base de données, et tout problème de requête se posera.
Dans le monde réel, vous ne pouvez pas toujours suivre la responsabilité à 100% . Si vous voulez faire encore mieux, vous pouvez utiliser une source de données à laquelle vous vous moquez. Pour nous (nous utilisons un ORM) cela ressemble à tester une technologie déjà existante. De plus, les tests deviennent beaucoup plus complexes et ne testent pas vraiment les requêtes. Donc, on continue comme ça.
Les données codées en dur sont stockées séparément dans les appareils. La fixture ressemble donc à un fichier SQL avec une instruction create table et des insertions pour les enregistrements que nous utilisons. Nous les gardons petits, sauf s’il est vraiment nécessaire de tester avec de nombreux enregistrements.
class ProductModel {
public function getAllActive() {
return $this->find('all', array('conditions' => array('active' => 1)));
}
}
Manette
Le contrôleur a besoin de plus de travail, car nous ne voulons pas tester le modèle avec. Donc, ce que nous faisons est de nous moquer du modèle. Cela signifie: Nous testons la méthode: index () qui devrait renvoyer une liste d'enregistrements.
Nous simulons donc la méthode de modèle getAllActive () et y ajoutons des données fixes (deux enregistrements, par exemple). Nous testons maintenant les données envoyées par le contrôleur à la vue et nous comparons si nous récupérons réellement ces deux enregistrements.
function testProductIndexLoggedIn() {
$this->setLoggedIn();
$this->ProductsController->mock('ProductModel', 'index', function(return array(your records) ));
$result=$this->ProductsController->index();
$this->assertEquals(2, count($result['products']));
}
C'est assez. Nous essayons d’ajouter le moins de fonctionnalités possibles au contrôleur car cela complique les tests. Mais bien sûr, il y a toujours du code dedans. Par exemple, nous testons des exigences telles que: Afficher ces deux enregistrements uniquement si vous êtes connecté.
Ainsi, le contrôleur a besoin d’une maquette normale et d’un petit fichier de données codées en dur. Pour un système de connexion, peut-être un autre. Dans notre test, nous avons une méthode d'assistance pour cela: setLoggedIn (). Cela facilite les tests avec ou sans connexion.
class ProductsController {
public function index() {
if($this->loggedIn()) {
$this->set('products', $this->ProductModel->getAllActive());
}
}
}
Les vues
Le test des vues est difficile. Nous séparons d’abord la logique qui se répète. Nous le mettons dans Helpers et testons ces classes de manière stricte. Nous attendons toujours le même résultat. Par exemple, generateHtmlTableFromArray ().
Nous avons ensuite des vues spécifiques au projet. Nous ne testons pas ceux-ci. Il n'est pas vraiment souhaitable de tester ceux-ci. Nous les gardons pour les tests d'intégration. Parce que nous avons extrait une grande partie du code, nous avons ici un risque moins élevé.
Si vous commencez à les tester, vous devrez probablement modifier vos tests chaque fois que vous modifiez un élément HTML, ce qui n’est pas utile pour la plupart des projets.
echo $this->tableHelper->generateHtmlTableFromArray($products);
Test d'intégration
Selon votre plate-forme, vous pouvez travailler avec des histoires d'utilisateurs, etc. Cela peut être basé sur le Web, comme Selenium ou d'autres solutions comparables.
Généralement, nous chargeons simplement la base de données avec les fixtures et affirmons quelles données devraient être disponibles. Pour les tests d'intégration complets, nous utilisons généralement des exigences très globales. Donc: définissez le produit sur actif, puis vérifiez si le produit devient disponible.
Nous ne testons pas tout à nouveau, par exemple si les champs appropriés sont disponibles. Nous testons les plus grandes exigences ici. Puisque nous ne voulons pas dupliquer nos tests à partir du contrôleur ou de la vue. Si quelque chose est vraiment essentiel / clé dans votre application ou pour des raisons de sécurité (le mot de passe de vérification n'est PAS disponible), nous les ajoutons pour nous assurer qu'il est correct.
Les données codées en dur sont stockées dans les appareils.
function testIntegrationProductIndexLoggedIn() {
$this->setLoggedIn();
$result=$this->request('products/index');
$expected='<table';
$this->assertContains($expected, $result);
// Some content from the fixture record
$expected='<td>Product 1 name</td>';
$this->assertContains($expected, $result);
}