2017-06-29 1 views
0

je commencé à utiliser PHPUnit pour les tests unitaires récemment et je structuré mes tests pour vérifier le comportement correct lorsqu'une fonction réussit et échoue.PHPUnit - Test Marquer comme échoué lorsque deux fonctions connexes ne

Par exemple, je pourrais tester la fonction connectToDevice() en espérant que lorsque la connexion réussit, elle renvoie true, sinon elle renvoie false. Ensuite, j'aurais:

public function testConnectToDeviceSuccess() 
{ 
    $this->assertTrue($this->myObject->connectToDevice()); 
} 

public function testConnectToDeviceFailure() 
{ 
    $this->assertFalse($this->myObject->connectToDevice()); 
} 

À l'heure actuelle une de ces fonctions nécessairement échouer, alors PHPUnit me rapport 1 échec. Au lieu de cela, je le définirais pour marquer le test échoué seulement lorsque les deux fonctions échouent. Donc quand 1 réussit, l'autre sera sauté.

Est-ce possible dans PHPUnit maintenir ces tests dans deux fonctions différentes?

+0

la seule fois où les deux échecs est quand vous avez une erreur sinon l'un d'entre eux est toujours vrai ou faux. Et si vous avez des erreurs dans les tests, le test ne se termine pas (seulement si vous n'attrapez pas l'erreur). – Edwin

+0

@Edwin le problème est que je ne veux pas avoir des dizaines de tests échoués qui étaient censés échouer. De plus, une fonction peut lancer une exception inattendue qui entraînera un échec pour les deux fonctions, donc l'échec du test. – DrKey

+0

alors vous faites quelque chose de mal, que ce soit dans les tests ou dans le développement. Les tests devraient toujours fonctionner, si vous voulez tester l'échec, configurez simplement votre environnement de telle sorte que vous êtes sûr qu'aucune connexion n'existe => connectToDevice sera faux. – Edwin

Répondre

1

Je pense que le problème que vous rencontrez est que vous manque une configuration qui garantit le résultat du test à droite. Cela peut sembler un peu bizarre, mais considèrent ce scénario

class ConnectedResource 
{ 
    private $connector; 

    public function __construct($connector) 
    { 
     $this->connector = $connector; 
    } 

    public function connectToDevice() 
    { 
     try { 
      $this->connector->connect(); 
     } catch (\Exception $e) { 
      return false; 
     } 

     return true; 
    } 
} 

qui est vraiment simplifiée, mais le bit est essentiel que vous voulez tester les deux chemins: connect() le succès et l'exception.

Qu'est-ce que vous feriez dans votre test est maintenant soit

public function testSuccessfulConnectReturnsTrue() 
{ 
    $connector = new Connector(
     // config to make a successful connection 
    ); 
    $myObject = new ConnectedResource($connector); 

    $this->assertTrue($myObject->connectToDevice()); 
} 

public function testFailedConnectReturnsFalse() 
{ 
    $connector = new Connector(
     // invalid config that will raise an exception 
    ); 
    $myObject = new ConnectedResource($connector); 

    $this->assertFalse($myObject->connectToDevice()); 
} 

Maintenant, les deux tests fonctionnent, car ils utilisent des connexions différentes (un qui fonctionne et qui ne fonctionne pas).

L'autre possibilité est de passer dans une maquette connecteur. En d'autres termes, au lieu d'un vrai connecteur, vous créez un objet fictif où vous pouvez décider vous-même ce que $this->connector->connect() retournera. Pensez-y comme quelque chose du genre "en supposant que j'ai un connecteur qui retourne vrai alors ConnectedResource devrait se comporter comme ça". Le code pour cela pourrait ressembler à ceci:

public function testSuccessfulConnectReturnsTrue() 
{ 
    $connector = $this->createMock(Connector::class); 
    $connector->expect($this->any()) 
     ->method('connect') 
     ->willReturn(true) 
    ; 
    $myObject = new ConnectedResource($connector); 

    $this->assertTrue($myObject->connectToDevice()); 
} 

et pour le scénario d'échec, il ressemblerait à quelque chose comme ceci:

public function testFailedConnectReturnsFalse() 
{ 
    $connector = $this->createMock(Connector::class); 
    $connector->expect($this->any()) 
     ->method('connect') 
     ->willReturn($this->throwException(new Exception)) 
    ; 
    $myObject = new ConnectedResource($connector); 

    $this->assertTrue($myObject->connectToDevice()); 
} 

Maintenant, vous contrôlez tout en dehors du cadre de l'essai (la connexion) et seulement tester le comportement défini à l'intérieur connectToDevice(). Cela rendra votre test sûr contre les changements dans l'autre classe, mais pourrait causer des problèmes lorsque, par exemple, dans une future mise à jour, la fonction connect() change, par ex. les arguments sont ajoutés ou modifiés.

Le bit important est que vous devez assurer que les exigences pour runnning votre test sont remplies.

1

Il est possible (vous pouvez faire un essai dépendra de l'issue d'un autre test, voir Test Dependencies), mais considérez ce que vous tester efficacement dans vos questions, emballage que dans un essai:

public function testConnectToDeviceBehavior() 
{ 
    $actual = $this->myObject->connectToDevice() === $this->myObject->connectToDevice(); 
    $this->assertTrue($actual); 
} 

Cela fera échouer le test si, et seulement si, la connexion à l'appareil ne donne pas deux fois le même résultat.

Vous pouvez écrire que des dépendances de test sur plusieurs tests, mais cela est très probablement pas ce que vous voulez tester à la fin.

Cependant, si, vérifiez la section liée des documents Phpunit.

Sinon, considérez ce qui est décrit dans the other answer, en utilisant l'injection de dépendances et testez votre objet avec une connexion de travail et une connexion défaillante au périphérique.