phpunit evita los argumentos del constructor para el simulacro

4 minutos de lectura

¿Cuál es la forma de evitar que phpunit tenga que llamar al constructor para un objeto simulado? De lo contrario, necesitaría un objeto simulado como argumento del constructor, otro para eso, etc. La API parece ser así:

getMock($className, $methods = array(), array $arguments = array(),
        $mockClassName="", $callOriginalConstructor = TRUE,
        $callOriginalClone = TRUE, $callAutoload = TRUE)

No consigo que funcione. Todavía se queja del argumento del constructor, incluso con $callOriginalConstructor establecido en falso.

Solo tengo un objeto en el constructor y es una inyección de dependencia. Así que no creo que tenga un problema de diseño allí.

avatar de usuario
david1010

Puedes usar getMockBuilder en lugar de solo getMock:

$mock = $this->getMockBuilder('class_name')
    ->disableOriginalConstructor()
    ->getMock();

Consulte la sección sobre “Dobles de prueba” en Documentación de PHPUnit para detalles.

Aunque puedes hacer esto, es mucho mejor no necesitarlo. Puede refactorizar su código para que, en lugar de inyectar una clase concreta (con un constructor), solo dependa de una interfaz. Esto significa que puede simular o simular la interfaz sin tener que decirle a PHPUnit que modifique el comportamiento del constructor.

  • Esto funciona muy bien para mí. Sin embargo, debería ser el ejemplo 10.3. Traté de editar la publicación, pero SO la rechazó por ser una edición demasiado corta.

    – Mateo

    31 de mayo de 2012 a las 13:34

  • Gracias @Lytithwyn. Actualicé la respuesta.

    – dave1010

    31 de mayo de 2012 a las 15:14

avatar de usuario
mateo purdon

Aquí tienes:

    // Get a Mock Soap Client object to work with.
    $classToMock = 'SoapClient';
    $methodsToMock = array('__getFunctions');
    $mockConstructorParams = array('fake wsdl url', array());
    $mockClassName="MyMockSoapClient";
    $callMockConstructor = false;
    $mockSoapClient = $this->getMock($classToMock,
                                     $methodsToMock,
                                     $mockConstructorParams,
                                     $mockClassName,
                                     $callMockConstructor);

  • Esto parece ser casi lo que quiero. Quiero llamar a getMock con solo la clase para burlarse y $callMockConstructor. ¿Cómo? algo como esto: $this->getMock($classToMock, $callMockConstructor). Lo único que se me ocurre es ir a la fuente de PHPUnit y cambiarlo a predeterminado = falso.

    – Gutzofter

    16 de abril de 2010 a las 18:53

  • Cambié el valor predeterminado a falso en testcase.php. Uno pensaría que estaría configurado como falso de forma predeterminada. Burlarse de un constructor parece muy extraño

    – Gutzofter

    16 de abril de 2010 a las 19:15

  • Excelente respuesta Justo lo que estaba buscando

    – hades

    18 de enero de 2013 a las 0:19

avatar de usuario
Wesley Gonçalves

Esta pregunta es un poco antigua, pero para los nuevos visitantes, puede hacerlo usando el createMock método (anteriormente llamado createTestDouble e introducido en v5.4.0).

$mock = $this->createMock($className);

Como puede ver en el siguiente código extraído de la PHPUnit\Framework\TestCase clase (en phpunit/src/framework/TestCase.php), básicamente creará un objeto simulado sin llamar al constructor original.

/** PHPUnit\Framework\TestCase::createMock method */
protected function createMock(string $originalClassName): MockObject
{
    return $this->getMockBuilder($originalClassName)
                ->disableOriginalConstructor()
                ->disableOriginalClone()
                ->disableArgumentCloning()
                ->disallowMockingUnknownTypes()
                ->getMock();
}

avatar de usuario
steve tauber

Como anexo, quería adjuntar expects() llama a mi objeto simulado y luego llama al constructor. En PHPUnit 3.7.14, el objeto que se devuelve cuando llama disableOriginalConstructor() es literalmente un objeto.

// Use a trick to create a new object of a class
// without invoking its constructor.
$object = unserialize(
sprintf('O:%d:"%s":0:{}', strlen($className), $className)

Desafortunadamente, en PHP 5.4 hay una nueva opción que no están usando:

ReflectionClass::nuevaInstanciaSinConstructor

Como esto no estaba disponible, tuve que reflejar manualmente la clase y luego invocar al constructor.

$mock = $this->getMockBuilder('class_name')
    ->disableOriginalConstructor()
    ->getMock();

$mock->expect($this->once())
    ->method('functionCallFromConstructor')
    ->with($this->equalTo('someValue'));

$reflectedClass = new ReflectionClass('class_name');
$constructor = $reflectedClass->getConstructor();
$constructor->invoke($mock);

Tenga en cuenta, si functionCallFromConstruct es protectedespecíficamente tienes que usar setMethods() para que se burle del método protegido. Ejemplo:

    $mock->setMethods(array('functionCallFromConstructor'));

setMethods() debe ser llamado antes de la expect() llamar. Personalmente, encadeno esto después disableOriginalConstructor() pero antes getMock().

avatar de usuario
Hans Wouters

Alternativamente, podría agregar un parámetro a obtenerMock para evitar la llamada del constructor predeterminado.

$mock = $this->getMock(class_name, methods = array(), args = array(), 
        mockClassName="", callOriginalConstructor = FALSE);

Aún así, creo que la respuesta de dave1010 se ve mejor, esto es solo para completar.

avatar de usuario
glenn musgo

Tal vez necesite crear un código auxiliar para pasarlo como argumento del constructor. Entonces puedes romper esa cadena de objetos simulados.

avatar de usuario
silenciado

PHPUnit está diseñado para llamar al constructor en objetos simulados; para evitar esto, debe:

  1. Inyecte un objeto simulado como una dependencia en el objeto del que tiene problemas para burlarse
  2. Cree una clase de prueba que amplíe la clase a la que intenta llamar que no llame al constructor principal

¿Ha sido útil esta solución?