¿Es posible, usando objetos simulados de PHPUnit, esperar una llamada a un método mágico __call()?

2 minutos de lectura

avatar de usuario
jeremy warne

Tengo un objeto simulado en una prueba. El objeto real, PageRepository, implementa un método mágico usando __call(), por lo que si llama a $pageRepository->findOneByXXXX($value_of_field_XXXX), buscará en la base de datos registros que coincidan con ese parámetro.

¿Hay alguna manera de burlarse de ese método?

La llamada al método real se vería así:

$homepage = $pageRepository->findOneBySlug('homepage');

La prueba se vería así:

$mockPageRepository->expects($this->any())
    ->method('findOneBySlug')
    ->will($this->returnValue(new Page()));

Pero no funciona: PHPUnit no detecta la llamada al método. La única forma de hacer que vea el método es definir el método explícitamente en PageRepository.

avatar de usuario
Juan Planitud

Unidad de PHP getMock() toma un segundo argumento, una matriz con los nombres de los métodos que se burlarán. Si incluye un nombre de método en esta matriz, el objeto simulado contendrá un método con ese nombre, que expects() y amigos trabajarán en.

Esto se aplica incluso a los métodos que no están definidos en la clase “real”, por lo que algo como lo siguiente debería funcionar:

$mockPageRepository = $this->getMock('PageRepository', array('findOneBySlug'));

Tenga en cuenta que deberá incluir explícitamente cualquier otro método que también deba simularse, ya que solamente los métodos nombrados en la matriz se definen para el objeto simulado.

  • Tenga en cuenta que, si necesita hacer cosas complejas a través de un constructor simulado (por ejemplo, deshabilitar el constructor original), entonces debe llamar setMethods() explícitamente por ejemplo $this->getMockBuilder('ClassName')->setMethods(['findById'])->getMock(); porque getMock() sobre el constructor no acepta argumentos.

    – JP

    4 de julio de 2016 a las 12:55

Encontré una mejor manera de hacer esto. Todavía no es perfecto, pero no tiene que especificar todos los métodos de clase de burla a mano, solo métodos mágicos:

    $methods = get_class_methods(PageRepository::class);
    $methods[] = 'findOneBySlug';
    $pageRepositoryMock = $this->getMockBuilder(PageRepository::class)
        ->disableOriginalConstructor()
        ->disableOriginalClone()
        ->disableArgumentCloning()
        ->disallowMockingUnknownTypes()
        ->setMethods($methods)
        ->getMock();

¿Ha sido útil esta solución?