Pergunta

Eu encontrei a discussão sobre Você testa método privado informativo.

Decidi que, em algumas aulas, quero ter métodos protegidos, mas testá -los. Alguns desses métodos são estáticos e curtos. Como a maioria dos métodos públicos faz uso deles, provavelmente poderei remover os testes com segurança posteriormente. Mas, para começar com uma abordagem de TDD e evitar a depuração, eu realmente quero testá -los.

Eu pensei no seguinte:

  • Objeto de método como aconselhado em uma resposta parece estar exagerado para isso.
  • Comece com métodos públicos e quando a cobertura do código for fornecida por testes de nível superior, vira -os protegidos e remova os testes.
  • Herdar uma aula com uma interface testável, tornando públicas métodos protegidos

Qual é a melhor prática? Mais alguma coisa?

Parece que o JUNIT muda automaticamente os métodos protegidos para serem públicos, mas eu não dei uma olhada mais profunda. PHP não permite isso via reflexão.

Foi útil?

Solução

Se você estiver usando o PHP5 (> = 5.3.2) com o PhPunit, poderá testar seus métodos privados e protegidos usando a reflexão para defini -los como públicos antes de executar seus testes:

protected static function getMethod($name) {
  $class = new ReflectionClass('MyClass');
  $method = $class->getMethod($name);
  $method->setAccessible(true);
  return $method;
}

public function testFoo() {
  $foo = self::getMethod('foo');
  $obj = new MyClass();
  $foo->invokeArgs($obj, array(...));
  ...
}

Outras dicas

Você parece já estar ciente, mas vou apenas reafirmar de qualquer maneira; É um sinal ruim, se você precisar testar métodos protegidos. O objetivo de um teste de unidade é testar a interface de uma classe e os métodos protegidos são detalhes da implementação. Dito isto, há casos em que faz sentido. Se você usar a herança, poderá ver uma superclasse como fornecendo uma interface para a subclasse. Então, aqui, você teria que testar o método protegido (mas nunca um privado 1). A solução para isso é criar uma subclasse para fins de teste e usá -la para expor os métodos. Por exemplo.:

class Foo {
  protected function stuff() {
    // secret stuff, you want to test
  }
}

class SubFoo extends Foo {
  public function exposedStuff() {
    return $this->stuff();
  }
}

Observe que você sempre pode substituir a herança pela composição. Ao testar o código, geralmente é muito mais fácil lidar com o código que usa esse padrão; portanto, você pode considerar essa opção.

TEASTBURN tem a abordagem correta. Ainda mais simples é chamar o método diretamente e retornar a resposta:

class PHPUnitUtil
{
  public static function callMethod($obj, $name, array $args) {
        $class = new \ReflectionClass($obj);
        $method = $class->getMethod($name);
        $method->setAccessible(true);
        return $method->invokeArgs($obj, $args);
    }
}

Você pode chamar isso simplesmente em seus testes:

$returnVal = PHPUnitUtil::callMethod(
                $this->object,
                '_nameOfProtectedMethod', 
                array($arg1, $arg2)
             );

Eu gostaria de propor uma ligeira variação para getMethod () definido em Resposta de Uckelman.

Esta versão muda getMethod () removendo valores codificados e simplificando um pouco o uso. Eu recomendo adicioná-lo à sua classe phpunitutil, como no exemplo abaixo ou na sua classe phpunit_framework_testCase-eliminamento (ou, suponho, globalmente ao seu arquivo phpunitutil).

Como o MyClass está sendo instanciado de qualquer maneira e o reflexoclass pode pegar uma string ou um objeto ...

class PHPUnitUtil {
    /**
     * Get a private or protected method for testing/documentation purposes.
     * How to use for MyClass->foo():
     *      $cls = new MyClass();
     *      $foo = PHPUnitUtil::getPrivateMethod($cls, 'foo');
     *      $foo->invoke($cls, $...);
     * @param object $obj The instantiated instance of your class
     * @param string $name The name of your private/protected method
     * @return ReflectionMethod The method you asked for
     */
    public static function getPrivateMethod($obj, $name) {
      $class = new ReflectionClass($obj);
      $method = $class->getMethod($name);
      $method->setAccessible(true);
      return $method;
    }
    // ... some other functions
}

Eu também criei uma função de alias getProtectedMethod () para ser explícita do que é esperado, mas isso depende de você.

Felicidades!

Eu acho que Troelskn está perto. Eu faria isso em vez disso:

class ClassToTest
{
   protected testThisMethod()
   {
     // Implement stuff here
   }
}

Em seguida, implemente algo assim:

class TestClassToTest extends ClassToTest
{
  public testThisMethod()
  {
    return parent::testThisMethod();
  }
}

Você então executa seus testes contra o teste TestClasStoTest.

Deve ser possível gerar automaticamente essas classes de extensão analisando o código. Eu não ficaria surpreso se a phpunit já oferece esse mecanismo (embora eu não tenha verificado).

Vou jogar meu chapéu no ringue aqui:

Eu usei o __call hack com graus mistos de sucesso. A alternativa que eu inventei foi usar o padrão do visitante:

1: Gere uma classe STDClass ou personalizada (para aplicar o tipo)

2: prepare isso com o método e argumentos necessários

3: Verifique se o seu SUT possui um método AcceptVisitor que executará o método com os argumentos especificados na classe visitante

4: Injete -o na classe que deseja testar

5: SUT injeta o resultado da operação no visitante

6: Aplique suas condições de teste ao atributo de resultado do visitante

Você pode realmente usar __call () de maneira genérica para acessar métodos protegidos. Para poder testar esta aula

class Example {
    protected function getMessage() {
        return 'hello';
    }
}

Você cria uma subclasse no ExempleTest.php:

class ExampleExposed extends Example {
    public function __call($method, array $args = array()) {
        if (!method_exists($this, $method))
            throw new BadMethodCallException("method '$method' does not exist");
        return call_user_func_array(array($this, $method), $args);
    }
}

Observe que o método __call () não faz referência à classe de forma alguma, para que você possa copiar o acima para cada classe com métodos protegidos que deseja testar e apenas alterar a declaração de classe. Você pode colocar essa função em uma classe base comum, mas eu não tentei.

Agora, o próprio caso de teste difere apenas em onde você constrói o objeto a ser testado, trocando em exemplo exposto, por exemplo.

class ExampleTest extends PHPUnit_Framework_TestCase {
    function testGetMessage() {
        $fixture = new ExampleExposed();
        self::assertEquals('hello', $fixture->getMessage());
    }
}

Acredito que o PHP 5.3 permite que você use a reflexão para alterar diretamente a acessibilidade dos métodos, mas presumo que você precise fazê -lo para cada método individualmente.

Eu sugiro seguir a solução alternativa para a solução alternativa/ideia de "Henrik Paul" :)

Você conhece nomes de métodos privados da sua classe. Por exemplo, eles são como _add (), _edit (), _delete () etc.

Portanto, quando você deseja testá-lo do aspecto do teste de unidade, basta ligar para métodos privados prefixando e/ou sufixando alguns comum palavra (por exemplo _addphpunit) para que, quando o método __call () é chamado (como o método _addphpunit () não existe) da classe proprietária, você apenas coloca o código necessário no método __call () para remover o método prefixado/sufixo (phpunit ) e depois chamar isso de método privado deduzido a partir daí. Este é outro bom uso de métodos mágicos.

Experimente.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top