41 votes

PHPUnit "La méthode simulée n'existe pas" lors de l'utilisation de $mock->expects($this->at(...))

J'ai rencontré un problème étrange avec les objets fantaisie de PHPUnit. J'ai une méthode qui devrait être appelée deux fois, donc j'utilise le matcheur "at". Cela fonctionne la première fois que la méthode est appelée, mais pour une raison quelconque, la deuxième fois qu'elle est appelée, j'obtiens "Mocked method does not exist". J'ai déjà utilisé le matcheur "at" et je n'ai jamais rencontré ce problème.

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 j'exécute le test, j'obtiens :

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 second matcheur, je n'obtiens pas l'erreur.

Quelqu'un a-t-il déjà rencontré ce problème ?

Merci de votre attention !

47voto

rr. Points 2268

Le problème s'est avéré être la façon dont j'ai compris le fonctionnement du matcheur "at". De plus, mon exemple n'était pas textuellement tel qu'il est dans mon test unitaire. Je pensais que le compteur de correspondance "at" fonctionnait par requête, alors qu'il fonctionne en réalité par instance d'objet.

Exemple :

class MyClass {

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

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

C'est faux :

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"));
    }

}

6 votes

Oui, mais je pense qu'il s'agit d'un bug dans PHPUnit. La documentation dit : Retourne un matcheur qui correspond lorsque la méthode pour laquelle il est évalué est invoquée à l'index $ donné.

10 votes

Je suis d'accord, et il serait beaucoup plus facile et utile d'espionner les appels de méthodes si l'index at() était incrémenté pour chaque méthode.

1 votes

Il semble qu'à peu près n'importe quelle utilisation erronée de expects provoquera le message "Mocked method does not exist" (la méthode simulée n'existe pas). Bon à savoir.

17voto

yvoyer Points 4028

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

L'erreur suivante s'est produite

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

Ceci corrige l'erreur

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

La même chose s'est produite lors de l'utilisation de l'option $this->exactly(0) méthode.

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

0 votes

Merci de votre attention ! J'ai rencontré le même problème et j'étais bloqué (jusqu'à ce que je lise votre commentaire).

0 votes

Merci, cela a également résolu mon problème. Mais je n'ai aucune idée de la raison pour laquelle cela a fonctionné. Pouvez-vous m'éclairer sur ce point ?

3 votes

@SilviuG, puisque la méthode n'est jamais censée être appelée, l'attente concernant les arguments "with" ne devrait jamais se produire. Donc, puisque l'attente configurée pour la méthode ne s'est pas produite mais a été configurée pour se produire (à cause du with), l'erreur est déclenchée. J'espère que cela a du sens. Je suis d'accord que le message est un peu bizarre pour cette erreur.

5voto

Alan Points 21

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

0 votes

Cela a fonctionné pour moi. J'ai appelé : une fois pour foo et 3 fois pour bar après foo. Démarrage bar attentes à partir d'un seul problème résolu.

4voto

Tyler Collier Points 1917

Il s'agit d'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 moi, d'après ce que je sais de mon propre code, je devrais utiliser at(0) y at(1) respectivement, mais ce n'est que lorsque j'ai utilisé la fonction at(2) y at(3) au lieu de cela, que cela a fonctionné. (J'utilise le session mocking dans CakePHP.)

La meilleure façon de vérifier l'ordre est d'entrer dans la méthode appelée et de vérifier ce qui a été transmis. Vous pouvez procéder de la manière suivante :

$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");
    })
);

0 votes

Bon sang, c'est ennuyeux ! Merci pour cela, j'avais vraiment du mal avec un test unitaire à cause de ce problème.

1voto

edorian Points 22780

Pour autant que je puisse en juger d'après le code de la démo, c'est devrait travail. J'ai produit un exemple fonctionnel au cas où vous utiliseriez une version plus ancienne de PHPUnit et que vous voudriez vérifier si cela fonctionne pour vous aussi.

Au cas où cela ne vous aiderait 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;
    }
}

l'impression

phpunit MyTest.php
PHPUnit 3.4.15 by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 4.25Mb

OK (1 test, 3 assertions)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X