Question

J'essaie de tester une classe qui gère l'accès aux données dans la base de données (vous savez, CRUD, essentiellement). La bibliothèque de base de données que nous utilisons possède une API dans laquelle vous obtenez d'abord l'objet table par un appel statique:

function getFoo($id) {
  $MyTableRepresentation = DB_DataObject::factory("mytable");
  $MyTableRepresentation->get($id);
  ... do some stuff
  return $somedata
}

... vous avez l'idée.

Nous essayons de tester cette méthode, mais nous nous moquons de l'objet DataObject pour que (a) nous n'ayons pas besoin d'une connexion réelle à la base de données pour le test et (b) nous n'avons même pas besoin d'inclure la lib DB_DataObject pour le test.

Cependant, dans PHPUnit, je ne peux apparemment pas obtenir que $ this- > getMock () mette correctement en place un appel statique. J'ai ...

        $DB_DataObject = $this->getMock('DB_DataObject', array('factory'));

... mais le test indique toujours une méthode inconnue "factory". Je sais que cela crée l'objet, car auparavant, il ne pouvait pas trouver DB_DataObject. Maintenant ça peut. Mais pas de méthode?

Ce que je veux vraiment faire, c'est avoir deux objets fictifs, un pour l'objet table également. Ainsi, non seulement je dois spécifier que la fabrique est un appel statique, mais également renvoyer un autre objet fictif spécifié que j'ai déjà configuré.

Je devrais mentionner comme mise en garde que je l'ai fait dans SimpleTest il y a quelque temps (je ne trouve pas le code) et que cela a bien fonctionné.

Qu'est-ce qui donne?

[UPDATE]

Je commence à comprendre que cela a quelque chose à voir avec expects ()

Était-ce utile?

La solution

Je conviens avec vous deux qu'il serait préférable de ne pas utiliser un appel statique. Cependant, j'imagine que j'ai oublié de mentionner que DB_DataObject est une bibliothèque tierce et que l'appel statique est leur meilleure pratique pour l'utilisation de leur code, pas la nôtre. Il existe d'autres manières d'utiliser leurs objets qui impliquent la construction directe de l'objet renvoyé. Il ne reste que ces instructions include / require comme quoi le fichier de classe utilise cette classe DB_DO. Ça craint parce que les tests vont casser (ou tout simplement ne pas être isolés) si vous essayez entre-temps de vous moquer d'une classe du même nom dans votre test - du moins je pense.

Autres conseils

Lorsque vous ne pouvez pas modifier la bibliothèque, modifiez-en l'accès. Refactorisez tous les appels à DB_DataObject :: factory () vers une méthode d'instance dans votre code:

function getFoo($id) {
  $MyTableRepresentation = $this->getTable("mytable");
  $MyTableRepresentation->get($id);
  ... do some stuff
  return $somedata
}

function getTable($table) {
  return DB_DataObject::factory($table);
}

Vous pouvez maintenant utiliser une maquette partielle de la classe que vous testez et faire en sorte que getTable () renvoie un objet table simulé.

function testMyTable() {
  $dao = $this->getMock('MyTableDao', array('getMock'));
  $table = $this->getMock('DB_DataObject', ...);
  $dao->expects($this->any())
      ->method('getTable')
      ->with('mytable')
      ->will($this->returnValue($table));
  $table->expects...
  ...test...
}

C’est un bon exemple de dépendance dans votre code - la conception a rendu impossible l’injection dans une maquette plutôt que dans la classe réelle.

Ma première suggestion serait d'essayer de refactoriser le code pour utiliser une instance plutôt qu'un appel statique.

Ce qui manque (ou pas?) à votre classe DB_DataObject, c’est un configurateur qui transmettra un objet de base de données préparé avant d’appeler la méthode factory. De cette façon, vous pouvez passer un objet fantaisie ou un objet de base de données personnalisé (avec la même interface) en cas de besoin.

Dans votre configuration de test:

 public function setUp() {
      $mockDb = new MockDb();
      DB_DataObject::setAdapter($mockDb);
 }

La méthode factory () doit renvoyer l'instance de base de données simulée. Si ce n’est pas déjà intégré dans votre classe, vous devrez probablement aussi refactoriser la méthode factory () pour que cela fonctionne.

Avez-vous besoin / y compris le fichier de classe pour DB_DataObject dans votre scénario de test? Si la classe n'existe pas avant que PHPUnit essaie de se moquer de l'objet, vous pouvez obtenir des erreurs comme celle-ci.

Avec l'extension PHPUnit MockFunction plus le kit de développement, vous pouvez également vous moquer des méthodes statiques. Soyez prudent, car il s'agit d'un patch de singe et ne devrait donc être utilisé que dans des cas extrêmes. Ne remplace pas les bonnes pratiques de programmation.

https://github.com/tcz/phpunit-mockfunction

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top