web-dev-qa-db-fra.com

Comment simuler une entrée php: // fr PHP?

J'écris un test unitaire pour mon projet PHP,

le test unitaire consiste à simuler une donnée php://input,

et j'ai lu le manuel, il dit:

php: // input est un flux en lecture seule qui vous permet de lire des données brutes à partir du corps de la demande.

Comment simuler le php://input ou écrire le corps de la requête dans mon PHP?


Voici mon code source et mon test unitaire, les deux sont simplifié .

La source :

class Koru
{
    static function build()
    {
        // This function will build an array from the php://input.
        parse_str(file_get_contents('php://input'), $input);

        return $input;
    }

    //...

Test de l'unité :

function testBuildInput()
{
    // Trying to simulate the `php://input` data here.
    // NOTICE: THIS WON'T WORK.
    file_put_contents('php://input', 'test1=foobar&test2=helloWorld');

    $data = Koru::build();

    $this->assertEquals($data, ['test1' => 'foobar',
                                'test2' => 'helloWorld']);
}
19
Yami Odymel

Voir Le paquet vfsStream et ce SO question et réponses .

En gros, vous voudriez paramétrer votre service qui lit les données pour accepter un chemin:

public function __construct($path)
{
    $data = file_get_contents($path); // you might want to use another FS read function here
}

Et ensuite, dans un test, indiquez un chemin de flux vfsStream:

\vfsStreamWrapper::register();
\vfsStream::setup('input');

$service = new Service('vfs://input') 

Dans votre code, vous fourniriez php://input comme d'habitude.

9
Finwe

Utilisez un double test

Étant donné le code dans la question, la solution la plus simple consiste à restructurer le code:

class Koru
{
    static function build()
    {
        parse_str(static::getInputStream(), $input);
        return $input;
    }

    /**
     * Note: Prior to PHP 5.6, a stream opened with php://input could
     * only be read once;
     *
     * @see http://php.net/manual/en/wrappers.php.php
     */
    protected static function getInputStream()
    {
        return file_get_contents('php://input');
    }

Et utilisez un double test:

class KoruTestDouble extends Koru
{
    protected static $inputStream;

    public static function setInputStream($input = '')
    {
        static::$inputStream = $input;
    }

    protected static function getInputStream()
    {
        return static::$inputStream;
    }
}

La méthode de test utilise ensuite le test double, pas la classe elle-même:

function testBuildInput()
{
    KoruTestDouble::setInputStream('test1=foobar&test2=helloWorld');

    $expected = ['test1' => 'foobar', 'test2' => 'helloWorld'];
    $result = KoruTestDouble::build();

    $this->assertSame($expected, $result, 'Stuff be different');
}

Évitez les classes statiques si possible

La plupart des difficultés rencontrées dans le scénario de la question résultent de l’utilisation de méthodes de classe statiques, ce qui rend les tests difficiles. Dans la mesure du possible, évitez d'utiliser des classes statiques et utilisez des méthodes d'instance qui permettent de résoudre le même type de problème en utilisant mock objects .

6
AD7six

Ce type de décomposition extrême ne gagne rien et conduit à un code très fragile. Vos tests doivent exprimer les attentes de vos interfaces, et non les données que vous leur avez fournies: PHP n'est-il vraiment pas libre de renvoyer ["test2"=>"helloWorld","test1"=>"foobar"] dans une version ultérieure? Est-ce que votre code est cassé si c'est le cas? Qu'est-ce que exactement pensez-vous tester?

Je pense que vous compliquez trop cela.

$a->doit devrait prendre $input en argument et ne pas appeler Koru::build dans le cadre de son initialisation. Ensuite, vous pouvez tester $a->doit au lieu de tester parse_str .

Si vous insistez pour suivre cet exemple, alors Koru::build doit prendre un argument de 'php://input' - il s'agit souvent de l'injection de dépendance, dans laquelle vous indiquez à vos fonctions tout ce qu'elles doivent savoir. Ensuite, lorsque vous souhaitez "tester" des choses, vous pouvez simplement transmettre un autre fichier (ou, par exemple, une data url ).

1
geocar

Avec Kahlan vous pouvez assigner la fonction file_get_contents directement comme ceci:

use My\Name\Space\Koru;

describe("::build()", function() {

    it("parses data", function() {

        allow('file_put_contents')->toBeCalled()->andRun(function() {
            return 'test1=foobar&test2=helloWorld';
        });
        expect(Koru::build())->toBe([
            'test1' => 'foobar',
            'test2' => 'helloWorld'
        ]);

    });

});
1
Jails

Utilisez un Zend\Diactoros\Stream

https://zendframework.github.io/zend-diactoros/usage/

$_POST['foo'] = 'bar';
use Zend\Diactoros\ServerRequestFactory;
$psrRequest = ServerRequestFactory::fromGlobals();
var_dump($psrRequest->getParsedBody()); // foo => bar
var_dump($_POST); // foo => bar

plus d'infos https://laracasts.com/discuss/channels/general-discussion/psr-7?page=1

1
fearis