Je travaille sur la mise en place d'une suite de tests pour un projet PHP Propel en utilisant Phactory, et PHPUnit. J'essaie actuellement de tester une unité qui fait une demande externe, et je veux boucher dans une réponse pour cette demande.Comment puis-je simuler une requête web externe dans PHPUnit?
Voici un extrait de la classe que je suis en train de tester:
class Endpoint {
...
public function parseThirdPartyResponse() {
$response = $this->fetchUrl("www.example.com/api.xml");
// do stuff and return
...
}
public function fetchUrl($url) {
return file_get_contents($url);
}
...
Et voici la fonction de test que je suis en train d'écrire.
// my factory, defined in a seperate file
Phactory::define('endpoint', array('identifier' => 'endpoint_$n');
// a test case in my endpoint_test file
public function testParseThirdPartyResponse() {
$phEndpoint = Phactory::create('endpoint', $options);
$endpoint = new EndpointQuery()::create()->findPK($phEndpoint->id);
$stub = $this->getMock('Endpoint');
$xml = "...<target>test_target</target>..."; // sample response from third party api
$stub->expects($this->any())
->method('fetchUrl')
->will($this->returnValue($xml));
$result = $endpoint->parseThirdPartyResponse();
$this->assertEquals('test_target', $result);
}
Je peux voir maintenant, après avoir essayé mon code de test, que je suis en train de créer un objet fantaisie avec getMock
, puis ne jamais l'utiliser. Donc la fonction fetchUrl
s'exécute réellement, ce que je ne veux pas. Mais je veux toujours être en mesure d'utiliser l'objet Phactory créé endpoint
, car il a tous les champs remplis à partir de ma définition d'usine.
Y a-t-il un moyen pour moi de boucher une méthode sur un objet existant? Donc, je pourrais talonner fetch_url
sur l'objet Endpoint $endpoint
que je viens de créer? Ou est-ce que je vais à ce sujet tout faux; Y a-t-il une meilleure façon pour moi de tester mon unité mes fonctions qui reposent sur des requêtes web externes? J'ai lu la documentation de PHPUnit concernant "Stubbing and Mocking Web Services", mais leur exemple de code pour le faire est de 40 lignes, sans avoir à définir votre propre wsdl. J'ai de la difficulté à croire que c'est la façon la plus commode pour moi de gérer cela, à moins que les bonnes gens de SO ne le ressentent autrement.
Appréciez grandement toute aide, j'ai été raccroché à ce sujet toute la journée. Merci!!
C'est ce que j'ai fini par faire, même si je n'en suis pas ravi. Une classe supplémentaire juste pour emballer 'file_get_contents' _feels_ comme exagéré pour moi. C'est un changement de code que je ferais seulement pour le tester, et ça me fait mal. Je viens de ruby, qui avait la gemme [webmock] (https://github.com/bblimke/webmock/), qui fait exactement ce que vous avez décrit dans votre dernier paragraphe: "crée un Webservice qui vit dans vos tests et agit comme un "Mock", renvoyant toujours des données préconfigurées ". Au lieu de coder moi-même je vais aller la route de l'objet maquette pour l'instant. Merci pour les pensées! – goggin13
Je ne pense pas que ce soit les frais généraux. Pensez-y comme ceci: Vous allez utiliser ce code dans de nombreux endroits. Dans un an, il y a une meilleure implémentation pour file_get_contents et vous devez le changer partout. Ou pensez que vous voulez passer à Buzz (que je recommanderais). Dans le code ci-dessus, vous injectez l'objet et vous devez modifier un appel de méthode. Peut-être que c'est un peu léger dans votre petit exemple, mais au fur et à mesure que votre application s'agrandit et que vous souhaiterez réutiliser file_get_contents, vous l'apprécierez peut-être. – Sgoettschkes