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.
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
@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
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