Prueba de clases abstractas

144

¿Cómo pruebo los métodos concretos de una clase abstracta con PHPUnit?

Esperaría tener que crear algún tipo de objeto como parte de la prueba. Sin embargo, no tengo idea de la mejor práctica para esto o si PHPUnit lo permite.

Mez
fuente
10
Quizás debería considerar cambiar la respuesta aceptada.
Jacob
1
Quizás stackoverflow.com/a/2947823/23963 ayudará.
Nigel Thorne

Respuestas:

240

La prueba unitaria de clases abstractas no significa necesariamente probar la interfaz, ya que las clases abstractas pueden tener métodos concretos, y estos métodos concretos pueden ser probados.

No es tan raro, cuando se escribe un código de biblioteca, tener cierta clase base que espera extender en su capa de aplicación. Y si desea asegurarse de que se prueba el código de la biblioteca, necesita medios para UT los métodos concretos de clases abstractas.

Personalmente, uso PHPUnit, y tiene los llamados trozos y objetos simulados para ayudarte a probar este tipo de cosas.

Directamente desde el manual de PHPUnit :

abstract class AbstractClass
{
    public function concreteMethod()
    {
        return $this->abstractMethod();
    }

    public abstract function abstractMethod();
}

class AbstractClassTest extends PHPUnit_Framework_TestCase
{
    public function testConcreteMethod()
    {
        $stub = $this->getMockForAbstractClass('AbstractClass');
        $stub->expects($this->any())
             ->method('abstractMethod')
             ->will($this->returnValue(TRUE));

        $this->assertTrue($stub->concreteMethod());
    }
}

El objeto simulado te da varias cosas:

  • no es necesario que tenga una implementación concreta de la clase abstracta, y en su lugar puede salirse con el código auxiliar
  • puede llamar a métodos concretos y afirmar que funcionan correctamente
  • Si el método concreto se basa en el método no implementado (abstracto), puede resguardar el valor de retorno con el método will () PHPUnit
Victor Farazdagi
fuente
38

Buena pregunta. He estado buscando esto también.
Afortunadamente, PHPUnit ya tiene un getMockForAbstractClass()método para este caso, por ejemplo

protected function setUp()
{
    $stub = $this->getMockForAbstractClass('Some_Abstract_Class');
    $this->_object = $stub;
}

Importante:

Tenga en cuenta que esto requiere PHPUnit> 3.5.4. Hubo un error en versiones anteriores.

Para actualizar a la versión más nueva:

sudo pear channel-update pear.phpunit.de
sudo pear upgrade phpunit/PHPUnit
tomashin
fuente
Suena interesante pero ¿estarías probando contra el simulacro? ¿Cómo serían las pruebas? IE: ¿extender el simulacro en caso de prueba y probar contra la clase de prueba extendida?
stefgosselin
34

Cabe señalar que a partir de PHP 7 se ha agregado soporte para clases anónimas . Esto le brinda una vía adicional para configurar una prueba para una clase abstracta, una que no depende de la funcionalidad específica de PHPUnit.

class AbstractClassTest extends \PHPUnit_Framework_TestCase
{
    /**
     * @var AbstractClass
     */
    private $testedClass;

    public function setUp()
    {
        $this->testedClass = new class extends AbstractClass {

            protected function abstractMethod()
            {
                // Put a barebones implementation here
            }
        };
    }

    // Put your tests here
}
GordonM
fuente
44
¡Gracias por esto! El uso de una clase anónima en PHPUnit me dio mucha flexibilidad para crear mis diversas pruebas.
Alice Wonder
1

Eran, tu método debería funcionar, pero va en contra de la tendencia de escribir la prueba antes del código real.

Lo que sugeriría es que escriba sus pruebas sobre la funcionalidad deseada de una subclase no abstracta de la clase abstracta en cuestión, luego escriba tanto la clase abstracta como la subclase de implementación, y finalmente ejecute la prueba.

Obviamente, sus pruebas deben probar los métodos definidos de la clase abstracta, pero siempre a través de la subclase.


fuente
Me parece una respuesta arbitraria: tienes una clase abstracta 'A' que tiene un método común 'foo ()'. Este método 'foo ()' se está utilizando en general en todas las clases 'B' y 'C', ambas derivan de 'A'. ¿Qué clase elegirías para probar 'foo ()'?
user3790897
1

La respuesta de Nelson es incorrecta.

Las clases abstractas no requieren que todos sus métodos sean abstractos.

Los métodos implementados son los que necesitamos probar.

Lo que puede hacer es crear una clase de código auxiliar falso en el archivo de prueba de la unidad, hacer que extienda la clase abstracta e implementar solo lo que se requiere sin ninguna funcionalidad, por supuesto, y probarlo.

Salud.

skqr
fuente
0

Si no desea subclasificar la clase abstracta solo para realizar una prueba unitaria de los métodos que ya están implementados en la clase abstracta, puede intentar ver si su marco le permite simular clases abstractas.

colgado
fuente