2010-07-29 13 views
35

Me he encontrado con un problema extraño con los objetos de simulación de PHPUnit. Tengo un método que debería llamarse dos veces, entonces estoy usando el matcher "at". Esto funciona por primera vez cuando se llama al método, pero por alguna razón, la segunda vez que se llama, obtengo "El método burlado no existe". He usado el matcher "at" antes y nunca me he encontrado con esto.

Mi código es algo como:

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

    ... 
} 

Cuando ejecuto la prueba, me sale:

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

Si quito el segundo de coincidencias, no me sale el error.

¿Alguien ha topado con esto antes?

Gracias!

Respuesta

37

El problema terminó siendo la forma en que entendí que el matcher "at" funcionaba. Además, mi ejemplo no era literalmente como está en mi prueba unitaria. Pensé que el contador de coincidencias "at" funcionaba por consulta, donde realmente funciona por instancia de objeto.

Ejemplo:

class MyClass { 

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

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

incorrecta:

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

} 

correcta:

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

} 
+5

Sí, pero creo que es un error en PHPUnit. La documentación dice: Devuelve un marcador que coincide cuando se invoca el método para el que se evalúa en el índice $ dado. – gphilip

+10

De acuerdo, además sería mucho más fácil y más útil espiar las llamadas a métodos si el índice at() se incrementara por método. –

+1

Parece que casi cualquier uso erróneo de las expectativas provocará el mensaje "El método simulado no existe". Bueno saber. –

1

Por lo que puedo decir por el código de demostración que debe trabajo. Produje un ejemplo de trabajo en caso de que esté ejecutando una versión anterior de PHPUnit y quiera verificarlo de esa manera si también funciona para usted.

En caso de que no sirva, ¿podría proporcionar un poco más de código (al mejor ejecutable)? :)

<?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; 
    } 
} 

imprimiendo

phpunit MyTest.php 
PHPUnit 3.4.15 by Sebastian Bergmann. 

. 

Time: 0 seconds, Memory: 4.25Mb 

OK (1 test, 3 assertions) 
0

¿Seguro que incluyó MiClase en su prueba? He tenido algunos errores de método no definidos al burlar una clase/interfaz sin incluirla.

+0

Sí, la primera matcher funciona bien, que es el rascador de cabeza –

16

FYI, no estoy seguro si es relacionado, pero me encontré con lo mismo, pero no con el Método $this->at(), para mí fue el método $this->never().

Esto planteó la error

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

Esto fija el error

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

Se hizo lo mismo cuando se utiliza el método $this->exactly(0).

Espero que esto ayude a alguien.

+0

¡Gracias! Esto fue realmente útil, me encontré con el mismo problema y me quedé atrapado (hasta que leí su comentario). –

+0

¡El mismo problema aquí, muchas gracias! – Mansiemans

+0

Gracias, eso solucionó mi problema también. Pero realmente no tengo idea de por qué funcionó. ¿Puedes arrojar algo de luz sobre el asunto? –

4

trate de cambiar $this->at(1)-$this->at(2)

+0

Funcionó para mí. He llamado: una vez para '' '' foo'''' y 3 veces para '' '' barra'''' métodos después de foo. Comenzando '' '' barra'''' expectativa de 1 problema solucionado. –

2

Ésta es una desafortunada redacción del mensaje de error por PHPUnit.

Revise el orden de sus llamadas, como la respuesta de @ rr.

Para mí, por lo que yo sé con mi propio código, que debería usar at(0) y at(1) respectivamente, pero no fue hasta que use at(2)at(3) y en lugar de que haya funcionado. (Estoy usando la burla de sesión en CakePHP.)

La mejor manera de verificar el pedido es "entrar" en el método llamado y verificar lo que pasó. Puede hacerlo de esta manera:

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