web-dev-qa-db-fra.com

comment tester un appel curl unitaire en php

Comment feriez-vous pour tester unement une implémentation curl?

  public function get() {
    $ch = curl_init($this->request->getUrl());

    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $result = curl_exec($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
    curl_close($ch);

    if (!strstr($type, 'application/json')) {
      throw new HttpResponseException('JSON response not found');
    }

    return new HttpResponse($code, $result);
  }

J'ai besoin de tester le type de contenu retourné pour qu'il puisse lever une exception.

41
Yader Hernandez

Comme l'a suggéré thomasrutter, créez une classe pour résumer l'utilisation des fonctions cURL.

interface HttpRequest
{
    public function setOption($name, $value);
    public function execute();
    public function getInfo($name);
    public function close();
}

class CurlRequest implements HttpRequest
{
    private $handle = null;

    public function __construct($url) {
        $this->handle = curl_init($url);
    }

    public function setOption($name, $value) {
        curl_setopt($this->handle, $name, $value);
    }

    public function execute() {
        return curl_exec($this->handle);
    }

    public function getInfo($name) {
        return curl_getinfo($this->handle, $name);
    }

    public function close() {
        curl_close($this->handle);
    }
}

Vous pouvez maintenant tester en utilisant une maquette de l'interface HttpRequest sans invoquer aucune des fonctions cURL.

public function testGetThrowsWhenContentTypeIsNotJson() {
    $http = $this->getMock('HttpRequest');
    $http->expects($this->any())
         ->method('getInfo')
         ->will($this->returnValue('not JSON'));
    $this->setExpectedException('HttpResponseException');
    // create class under test using $http instead of a real CurlRequest
    $fixture = new ClassUnderTest($http);
    $fixture->get();
}

Modifier Correction simple PHP erreur d'analyse.

54
David Harkness

N'utilisez pas curl directement mais via un wrapper tel que PEAR HTTP_Request2 . Avec lui, vous avez la possibilité d'échanger le pilote de boucle avec un pilote de simulation - idéal pour les tests unitaires.

7
cweiske

Vous pouvez utiliser une bibliothèque de simulation de fonction. J'en ai fait un pour vous: php-mock-phpunit

namespace foo;

use phpmock\phpunit\PHPMock;

class BuiltinTest extends \PHPUnit_Framework_TestCase
{

    use PHPMock;

    public function testCurl()
    {
        $curl_exec = $this->getFunctionMock(__NAMESPACE__, "curl_exec");
        $curl_exec->expects($this->once())->willReturn("body");

        $ch = curl_init();
        $this->assertEquals("body", curl_exec($ch));
    }
}
6
Markus Malkusch

Je suis tombé sur cette question lorsque j'essayais de tester moi-même une classe en utilisant cURL. J'ai pris à cœur les conseils de David Harkness et créé une interface pour cURL. Cependant, la fonctionnalité stub/mock fournie par PHPUnit n'était pas suffisante dans mon cas, j'ai donc ajouté ma propre implémentation de stub de l'interface et mis tout cela sur GitHub. Et parce que cette question apparaît assez tôt sur Google lors de la recherche sur ce problème, j'ai pensé la publier ici, afin que les autres puissent économiser l'effort.

Le voici.

Le wiki du dépôt contient une documentation assez détaillée des capacités du stub, mais les voici en bref.

L'interface est un mappage 1: 1 des fonctions cURL de PHP, afin de faciliter l'utilisation de l'interface (remettez simplement votre ClassUnderTest une instance implémentant SAI_CurlInterface puis appelez toutes les fonctions cURL comme précédemment, mais en tant que méthodes sur cette instance). La classe SAI_Curl implémente cette interface en déléguant simplement à cURL. Maintenant, si vous voulez tester le ClassUnderTest, vous pouvez lui donner une instance de SAI_CurlStub.

Le stub atténue principalement le problème selon lequel les mocks et stubs de PHPUnit ne peuvent pas renvoyer de données factices en fonction des anciens appels de fonction (mais c'est ainsi que cURL fonctionne réellement - vous configurez vos options et la réponse, le code d'erreur et les informations cURL dépendent de ces options). Voici donc un court exemple, montrant ces capacités de réponses (pour les codes d'erreur et les informations cURL, voir le wiki).

public function testGetData()
{
    $curl = new SAI_CurlStub();

    // Set up the CurlStub
    $defaultOptions = array(
        CURLOPT_URL => 'http://www.myserver.com'
    );
    $chromeOptions = array(
        CURLOPT_URL => 'http://www.myserver.com',
        CURLOPT_USERAGENT => 'Chrome/22.0.1207.1'
    );
    $safariOptions = array(
        CURLOPT_URL => 'http://www.myserver.com',
        CURLOPT_USERAGENT => 'Safari/537.1'
    );

    $curl->setResponse('fallback response');
    $curl->setResponse('default response from myserver.com'
                       $defaultOptions);
    $curl->setResponse('response for Chrome from myserver.com',
                       $chromeOptions);
    $curl->setResponse('response for Safari from myserver.com',
                       $safariOptions);

    $cut = new ClassUnderTest($curl);

    // Insert assertions to check whether $cut handles the
    // different responses correctly
    ...
}

Vous pouvez faire dépendre votre réponse de toute combinaison d'options cURL. Bien sûr, vous pouvez aller encore plus loin. Par exemple, votre ClassUnderTest prend des données XML d'un serveur et les analyse (enfin, vous devriez avoir deux classes distinctes pour ces tâches, mais supposons cela pour notre exemple), et vous voulez tester ce comportement . Vous pouvez télécharger la réponse XML manuellement et demander à votre test de lire les données du fichier et de les insérer dans la réponse. Vous savez alors exactement quelles données se trouvent et pouvez vérifier si elles sont correctement analysées. Vous pouvez également implémenter le SAI_CurlInterface chargement immédiat de toutes les réponses de votre système de fichiers, mais l'implémentation existante est définitivement un point de départ.

Au moment où j'écris cette réponse, @ SAI_CurlStub @ ne prend pas encore en charge les fonctionnalités multi-lib cURL, mais je prévois de l'implémenter également à l'avenir.

J'espère que ce talon sera utile à tous ceux qui veulent tester les classes dépendantes de cURL. N'hésitez pas à consulter et à utiliser les cours, ou à contribuer, bien sûr - c'est sur GitHub après tout :). En outre, je suis ouvert à toute critique constructive concernant la mise en œuvre et l'utilisation de l'interface et du stub.

3
Martin Ender

Une approche consiste à remplacer l'interface que vous utilisez (dans ce cas, les fonctions curl_) par des versions factices d'elles-mêmes qui renvoient certaines valeurs. Si vous utilisiez une bibliothèque orientée objet, ce serait plus facile car vous pourriez simplement remplacer un objet factice qui a les mêmes noms de méthode (et en effet, des frameworks comme simpletest peuvent facilement configurer des méthodes d'objet factice). Sinon, il y a peut-être une autre sorcellerie que vous pouvez utiliser pour remplacer les fonctions intégrées par des mannequins. Cette extension inclut override_function () qui ressemble à ce dont vous auriez besoin, bien que cela ajouterait une autre dépendance.

Si vous voulez tester cela sans remplacer les fonctions curl_ par des versions factices, il semble que vous devrez configurer un serveur factice qui retournera un certain résultat, afin que vous puissiez tester la façon dont votre PHP, et son extension curl, gère ce résultat. Pour le tester complètement, vous devez y accéder via HTTP plutôt que, disons, un fichier local, car votre PHP dépend de la présence d'un code de réponse HTTP, etc. Donc, vos tests auront besoin un serveur HTTP fonctionnel.

Par ailleurs, PHP 5.4 inclura en fait son propre serveur Web qui serait utile à cet effet. Sinon, vous pourriez mettre un script de test sur un serveur connu que vous contrôlez, ou distribuer une simple configuration de serveur avec vos tests.

Si vous deviez réellement utiliser le serveur en direct pour vos tests, cela deviendrait moins un test unitaire et plus un test d'intégration, car vous testez à la fois votre PHP et le serveur, et l'intégration entre les deux. Vous pourriez également manquer de pouvoir tester à la demande comment votre code gère certains échecs.

2
thomasrutter