Testando Classes Abstratas

144

Como testar os métodos concretos de uma classe abstrata com PHPUnit?

Eu esperaria que eu tivesse que criar algum tipo de objeto como parte do teste. No entanto, não faço ideia da melhor prática para isso ou se o PHPUnit permitir isso.

Mez
fonte
10
Talvez você deva considerar mudar a resposta aceita.
Jacob
1
Talvez o stackoverflow.com/a/2947823/23963 ajude.
Nigel Thorne

Respostas:

240

O teste de unidade de classes abstratas não significa necessário testar a interface, pois as classes abstratas podem ter métodos concretos e esses métodos concretos podem ser testados.

Não é tão incomum, ao escrever um código de biblioteca, ter certa classe base que você espera estender na camada de aplicativos. E se você quiser garantir que o código da biblioteca seja testado, precisará de meios para UT os métodos concretos das classes abstratas.

Pessoalmente, eu uso o PHPUnit e ele chama stubs e objetos simulados para ajudá-lo a testar esse tipo de coisa.

Diretamente do manual do 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());
    }
}

O objeto simulado oferece várias coisas:

  • você não é obrigado a ter uma implementação concreta da classe abstrata e pode se safar do stub
  • você pode chamar métodos concretos e afirmar que eles funcionam corretamente
  • se o método concreto se basear no método não implementado (abstrato), você pode stub o valor de retorno com o método will () PHPUnit
Victor Farazdagi
fonte
38

Esta é uma boa pergunta. Eu também estava procurando por isso.
Felizmente, o PHPUnit já possui um getMockForAbstractClass()método para este caso, por exemplo

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

Importante:

Note que isso requer PHPUnit> 3.5.4. Houve um erro nas versões anteriores.

Para atualizar para a versão mais recente:

sudo pear channel-update pear.phpunit.de
sudo pear upgrade phpunit/PHPUnit
leva
fonte
Parece interessante, mas você estaria testando contra o mock? Como seriam os testes? IE: estendendo a simulação no caso de teste e testando contra a classe de teste estendida?
21411 stefgosselin
34

Deve-se notar que, a partir do PHP 7 , foi adicionado suporte para classes anônimas . Isso fornece uma avenida adicional para a configuração de um teste para uma classe abstrata, que não depende da funcionalidade específica do 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
fonte
4
Obrigado por isso! Usar uma classe anônima no PHPUnit me deu muita flexibilidade na criação de meus vários testes.
Alice maravilha
1

Eran, seu método deve funcionar, mas vai contra a tendência de escrever o teste antes do código real.

O que eu sugeriria é escrever seus testes na funcionalidade desejada de uma subclasse não abstrata da classe abstrata em questão, depois escrever a classe abstrata e a subclasse de implementação e, finalmente, executar o teste.

Seus testes devem obviamente testar os métodos definidos da classe abstrata, mas sempre através da subclasse.


fonte
Encontro como resposta arbitrária: Você tem uma classe abstrata 'A' com um método 'foo ()' comum. Esse método 'foo ()' está sendo usado no geral em todas as classes 'B' e 'C', ambas derivadas de 'A'. Qual classe você escolheria testar 'foo ()'?
user3790897
1

A resposta de Nelson está errada.

As classes abstratas não exigem que todos os seus métodos sejam abstratos.

Os métodos implementados são os que precisamos testar.

O que você pode fazer é criar uma classe de stub falsa no arquivo de teste da unidade, estender a classe abstrata e implementar apenas o necessário, sem nenhuma funcionalidade, é claro, e testá-lo.

Felicidades.

skqr
fonte
0

Se você não deseja subclassificar a classe abstrata apenas para executar um teste de unidade nos métodos já implementados na classe abstrata, tente verificar se sua estrutura permite zombar de classes abstratas.

enforcado
fonte