web-dev-qa-db-fra.com

PHPUnit "La méthode simulée n'existe pas." en utilisant $ mock-> expectes ($ this-> at (...))

J'ai rencontré un problème étrange avec les objets fictifs PHPUnit. J'ai une méthode qui devrait être appelée deux fois, donc j'utilise le matcher "at". Cela fonctionne pour la première fois que la méthode est appelée, mais pour une raison quelconque, la deuxième fois, je reçois le message "La méthode simulée n'existe pas.". J'ai déjà utilisé le "at" matcher et je ne l'ai jamais vu.

Mon code ressemble à quelque chose comme:

class MyTest extends PHPUnit_Framework_TestCase
{
    ...

    public function testThis()
    {
        $mock = $this->getMock('MyClass', array('exists', 'another_method', '...'));
        $mock->expects($this->at(0))
             ->method('exists')
             ->with($this->equalTo('foo'))
             ->will($this->returnValue(true));

        $mock->expects($this->at(1))
             ->method('exists')
             ->with($this->equalTo('bar'))
             ->will($this->returnValue(false));
    }

    ...
}

Lorsque je lance le test, je reçois:

Expectation failed for method name is equal to <string:exists> when invoked at sequence index 1.
Mocked method does not exist.

Si je supprime le deuxième matcher, je ne reçois pas l'erreur.

Quelqu'un a-t-il déjà couru ça?

Merci!

37
rr.

Le problème a fini par être avec la façon dont j'ai compris que le matcher "at" devait fonctionner. De plus, mon exemple n'était pas mot pour mot comment c'est dans mon test unitaire. Je pensais que le compteur "at" fonctionnait sur une base par requête, où il fonctionnait réellement sur une instance par objet.

Exemple:

class MyClass {

    public function exists($foo) {
        return false;
    }

    public function find($foo) {
        return $foo;
    }
}

Incorrect:

class MyTest extends PHPUnit_Framework_TestCase
{

    public function testThis()
    {
        $mock = $this->getMock('MyClass');
        $mock->expects($this->at(0))
             ->method('exists')
             ->with($this->equalTo('foo'))
             ->will($this->returnValue(true));

        $mock->expects($this->at(0))
             ->method('find')
             ->with($this->equalTo('foo'))
             ->will($this->returnValue('foo'));

        $mock->expects($this->at(1))
             ->method('exists')
             ->with($this->equalTo('bar'))
             ->will($this->returnValue(false));

        $this->assertTrue($mock->exists("foo"));
        $this->assertEquals('foo', $mock->find('foo'));
        $this->assertFalse($mock->exists("bar"));
    }

}

Correct:

class MyTest extends PHPUnit_Framework_TestCase
{

    public function testThis()
    {
        $mock = $this->getMock('MyClass');
        $mock->expects($this->at(0))
             ->method('exists')
             ->with($this->equalTo('foo'))
             ->will($this->returnValue(true));

        $mock->expects($this->at(1))
             ->method('find')
             ->with($this->equalTo('foo'))
             ->will($this->returnValue('foo'));

        $mock->expects($this->at(2))
             ->method('exists')
             ->with($this->equalTo('bar'))
             ->will($this->returnValue(false));

        $this->assertTrue($mock->exists("foo"));
        $this->assertEquals('foo', $mock->find('foo'));
        $this->assertFalse($mock->exists("bar"));
    }

}
42
rr.

Pour votre information, je ne sais pas si c'est lié, mais j'ai rencontré la même chose, mais pas avec la méthode $this->at(); pour moi, c'était la méthode $this->never().

Cela a soulevé l'erreur

$mock->expects($this->never())
    ->method('exists')
    ->with('arg');

Cela a corrigé l'erreur

$mock->expects($this->never())
    ->method('exists');  

Il a fait la même chose avec la méthode $this->exactly(0).

J'espère que cela aidera quelqu'un.

16
yvoyer

Essayez de changer $this->at(1) en $this->at(2)

5
Alan

Ceci est une formulation malheureuse du message d'erreur de PHPUnit.

Vérifiez l’ordre de vos appels, comme le mentionne la réponse de @ rr.

Pour ma part, autant que je sache avec mon propre code, je devrais utiliser respectivement at(0) et at(1), mais ce n'est que lorsque j'ai utilisé at(2) et at(3) à la place que cela a fonctionné. (J'utilise une session moqueuse dans CakePHP.)

Le meilleur moyen de vérifier l'ordre est d'entrer 'dans' la méthode appelée et de vérifier ce qui est passé. Vous pouvez faire ça comme ça:

$cakePost = $this->getMock('CakePost');
$cakePost->expects($this->once())
->method('post')
->with(
    // Add a line like this for each arg passed
    $this->callback(function($arg) {
        debug("Here's what was passed: $arg");
    })
);
3
Tyler Collier

Autant que je puisse dire à partir du code de démonstration, il devrait fonctionne. J'ai produit un exemple de travail au cas où vous utiliseriez une ancienne version de PHPUnit et que vous souhaitiez vérifier si cela fonctionnait pour vous aussi.

Si cela ne vous aide pas, vous pourriez peut-être fournir un peu plus de code (au mieux exécutable)? :)

<?php

class MyTest extends PHPUnit_Framework_TestCase
{

    public function testThis()
    {
        $mock = $this->getMock('MyClass');
        $mock->expects($this->at(0))
             ->method('exists')
             ->with($this->equalTo('foo'))
             ->will($this->returnValue(true));

        $mock->expects($this->at(1))
             ->method('exists')
             ->with($this->equalTo('bar'))
             ->will($this->returnValue(false));

        $this->assertTrue($mock->exists("foo"));
        $this->assertFalse($mock->exists("bar"));
    }

}

class MyClass {

    public function exists($foo) {
        return false;
    }
}

impression

phpunit MyTest.php
PHPUnit 3.4.15 by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 4.25Mb

OK (1 test, 3 assertions)
1
edorian

Êtes-vous sûr d'avoir inclus MyClass dans votre test? J'ai eu des erreurs de méthode non définies en moquant une classe/interface sans l'inclure.

0
martinvium

Peut-être pas quand la question a été soulevée mais aujourd'hui documentation spécifie clairement comment l’at est à utiliser et je cite

Remarque
Le paramètre $ index du matcher at () fait référence à l'index, à partir de zéro, dans tous les appels de méthodes pour un objet factice donné. Faites preuve de prudence lorsque vous utilisez cet adaptateur car cela peut conduire à des tests fragiles trop étroitement liés à des détails d'implémentation spécifiques.

0
Mubashar Ahmad