2009-11-05 4 views
6

J'essaie de tester si une méthode protégée est appelée dans une interface publique. Je sais que ça s'appelle correctement, mais PHPUnit dit son jamais appelé.Test si une méthode protégée a été appelée

La même chose se produit quand je teste l'inverse, lorsqu'une méthode est jamais appelé:

<?php 
abstract class AnotherClassAbstract 
{ 
    abstract public foo(); 

    public function doAnotherStuff() 
    {  
     $this->_loadCache(); 
    } 

    protected function _loadCache(); 
    { 
     // implementation is irrelevant 
    } 
} 

<?php 
class MyTest extends PHPUnit_Framework_TestCase 
{ 
    public function testCalled() 
    { 
     $mock = $this->getMockForAbstractClass('AnotherClass'); 
     $mock->expects($this->once()) 
      ->method('_loadCache'); 

     $mock->doAnotherStuff(); 
    } 
} 

La méthode est appelée, mais PHPUnit dit que ce n'est pas.

Qu'est-ce qui ne va pas?

Modifier Je n'étais pas déclarer mes méthodes avec des deux-points, il était juste pour dénoter qu'il était une méthode publique (interface). Mise à jour vers les déclarations de classe/méthodes complètes.

Edit 2 Je aurais dû dire que je suis tester certaines implémentations de méthode dans une classe abstraite (modifié le code pour en tenir compte). Puisque je ne peux pas instancier la classe, comment puis-je tester cela?

Je pense à créer un SomeClassSimple en étendant SomeClassAbstract et en testant celui-ci à la place. Est-ce la bonne approche?

Répondre

11

Dans la version de PHPUnit que j'ai, la classe PHPUnit_Framework_MockObject_Mock utilise le get_class_methods de PHP pour déterminer l'interface de l'objet en train d'être raillé. get_class_methods choisira seulement les méthodes publiques, pas les méthodes privées ou protégées.

Ceci est conforme à l'esprit des tests unitaires xUnit. Considérez l'exemple de docs PHPUnit sur comment utiliser Mock Objects. Là, le SUT est Subject, qui a une méthode protégée notify. La méthode testée est cependant doSomething. Considérant Subject comme une boîte noire, nous ne nous soucions pas des détails de la façon dont il est mis en œuvre. Nous nous soucions, cependant, de son comportement . Plus précisément, nous demandons que lorsque nous appelons doSomething, la méthode update de tous les observateurs attachés est appelée. Nous nous moquons donc de l'objet tiers Observer, et nous nous attendons à ce que sa méthode update soit appelée. Notez que bien que la méthode protégée notify soit exercée, elle n'est pas nommée explicitement dans le test. Cela nous donne la liberté de changer l'implémentation à tout moment, tant que le comportement est préservé.

Revenons à votre exemple.

class MyTest extends PHPUnit_Framework_TestCase 
{ 
    public function testCalled() 
    { 
    $mock = $this->getMock('SomeClass'); 
    $mock->expects($this->once()) 
     ->method('_protectedMethod'); 

    $mock->doStuff(); 
    } 
} 

Ici, votre méthode testCalled crée aucune instance du système sous test (SUT), qui serait SomeClass. La version de doStuff dans la maquette de SomeClass ne fait rien. En particulier, il n'appelle pas _protectedMethod.

+0

Merci Ewan, vous avez raison.Bien sûr, si c'est un objet mock il n'a pas l'implémentation de la méthode. Comme je suis bête. Je vais marquer votre réponse comme correcte. Si vous le pouvez, s'il vous plaît, voir ma deuxième édition. –

+0

Donc, ce que j'essaie de tester est la mise en œuvre, pas le comportement, non? C'est un peu plus clair pour moi, merci. –

+2

Les tests unitaires sont, je pense, plus d'un art qu'il n'y paraît. Je l'ai apprécié plus après avoir lu les modèles de test xUnit: Refactoring Test Code par Gerard Meszaros. À son tour, le livre était une forte recommandation de Sebastian Bergmann, l'auteur de PHPUnit. –

4

Il semble que vous avez simplement besoin de dire PHPUnit que vous voulez se moquer de cette fonction, i.e. .:

$mock = $this->getMock('SomeClass', 
         array('_protectedMethod')); 
$mock->expects($this->once()) 
    ->method('_protectedMethod'); 

$mock->doStuff(); 
Questions connexes