2011-01-15 12 views
17

Quiero burlarme de un método de una clase y ejecutar una devolución de llamada que modifique el objeto dado como parámetro (usando PHP 5.3 con PHPUnit 3.5.5).Modificar objetos en returnCallback() de PHPUnit Mocks

Vamos a decir que tengo la clase siguiente:

class A 
{ 
    function foobar($object) 
    { 
    doSomething(); 
    } 
} 

Y este código de configuración:

$mock = $this->getMockBuilder('A')->getMock(); 
$mock->expects($this->any())->method('foobar')->will(
    $this->returnCallback(function($object) { 
    $object->property = something; 
    })); 

Por alguna razón, el objeto no quede modificado. En var_dump ing $object Veo que es el objeto correcto. ¿Podría ser que el objeto se pasa por valor? ¿Cómo puedo configurar el simulacro para recibir una referencia?

Respuesta

39

Él Alex,

i hablado con Sebastian (el creador PHPUnit) sobre ese problema y sí: El argumento Obtiene clone ed antes de pasar a la devolución de llamada.

Desde lo alto de mi cabeza no puedo ofrecer ninguna solución, pero elijo responder de todos modos para al menos decirte que no estás haciendo nada mal y que este es el comportamiento esperado.

Para citar comentario de Sebastian en el IRC sobre por qué se clona el argumento:

Es un largo debate en curso entre yo, yo mismo, y los usuarios de PHPUnit si es o no es correcto ;-)

Para tener algo copy/pasteable:

Assertion 3 en este codesample fallará. (La variable sólo se cambia en el objeto devuelto)

<?php 
class A 
{ 
    function foobar($o) 
    { 
     $o->x = mt_rand(5, 100); 
    } 
} 

class Test extends PHPUnit_Framework_TestCase 
{ 
    public function testFoo() 
    { 
     $mock = $this->getMock('A'); 
     $mock->expects($this->any()) 
      ->method('foobar') 
      ->will($this->returnCallback(function($o) { $o->x = 2; return $o; })); 

     $o = new StdClass; 
     $o->x = 1; 

     $this->assertEquals(1, $o->x); 
     $return = $mock->foobar($o); 

     $this->assertEquals(2, $return->x); 
     $this->assertEquals(2, $o->x); 
    } 
} 

Actualización:

A partir de PHPUnit 3.7 la clonación se puede apagar. Ver el último argumento fuera:

public function getMock(
    $originalClassName, 
    $methods = array(), 
    array $arguments = array(), 
    $mockClassName = '', 
    $callOriginalConstructor = TRUE, 
    $callOriginalClone = TRUE, 
    $callAutoload = TRUE, 
    $cloneArguments = FALSE 
); 

Incluso podría ser desactivada por defecto :)

+2

¡Muchas gracias por esta respuesta detallada y bien investigada! Aunque ahora tengo que pensar en otra forma de probar mi situación, es bueno saber que este es el comportamiento esperado. –

5

La clase que lleva a cabo la clonación de los parámetros pasados ​​al método burlado es PHPUnit_Framework_MockObject_Invocation_Static. Si mira cloneObject(), verá que devolverá el objeto original si el método de la clase del parámetro __clone() no es público.

Si usted tiene control sobre la clase de los parámetros de objetos y que no es necesario que alguna vez los clonar a sí mismo, se puede añadir un método privado vacío __clone().

+0

Ese truco no se ve muy bien, pero funcionó para mí, ¡muchas gracias! –

Cuestiones relacionadas