Phpunit: Provedores de dados são executados antes de setupBeforeClass

Criado em 21 fev. 2013  ·  21Comentários  ·  Fonte: sebastianbergmann/phpunit

Os provedores de dados são chamados antes que setupBeforeClass estático seja executado. Se pensar que deveria ser o contrário.

Caso de uso:

  • Temos uma lista de adaptadores. Alguns adaptadores são suportados apenas em alguns ambientes (ou seja, Linux vs Win),
  • Eu gostaria de inicializar uma lista de adaptadores suportados uma vez e passá-la para todos os testes por meio de um provedor de dados,
  • A maneira mais limpa que posso imaginar é inicializar uma propriedade estática em setupBeforeClass e fazer com que o provedor retorne essa propriedade

O problema é que a propriedade não é inicializada quando o provedor é chamado.

Versão encontrada: 3.7.14

Comentários muito úteis

@epdenouden JIT? Você está testando com JIT para PHP ou há algo que não sei? Desculpe, estou um pouco confuso; você está falando sobre PHP7.x e PHP8.x ou PHPUnit7.x e PHPUnit8.x ?!

@MAChitgarha Eu entendo sua confusão porque o PHP8 está recebendo um _compiler_ just-in-time. Eu estava usando o termo para a implementação do novo

Isso funcionará bem com o próximo JIT do PHP versão 8, pois não usa nenhum recurso complicado da própria linguagem PHP. Todo o meu trabalho está profundamente dentro do PHPUnit, refazendo coisas como análise de configuração, carregamento e execução de teste, etc.

Se parece que não estou fazendo nada, estou fazendo certo. Exceto para aquela fatura reduzida do GCP ou AWS. 💸

Todos 21 comentários

Não tenho certeza se impor alguma ordem para que os recursos estáticos possam ser inicializados em setupBeforeClass realmente faça sentido se você considerar que os provedores de dados não precisam ser estáticos ou mesmo na mesma classe de teste.

Por que não inicializar lentamente os adaptadores? Então, a ordem não importa em absoluto.

Sem executar o provedor de dados, não sabemos quantos testes um método de teste executa. Em um mundo perfeito, quebraríamos o B / C e exigiríamos que os provedores de dados fossem objetos que implementassem uma interface que estendesse Countable . Assim poderíamos separar essas preocupações e ter uma situação muito mais limpa.

@whatthejeff Isso é o que acabamos fazendo. No entanto, a inicialização no construtor pode ser um pouco mais limpa, porque os parâmetros devem ser agrupados em uma matriz quando retornados de um provedor.

@sebastianbergmann ok entendo porque são chamados antes, que tal citar no doc? (Eu posso abrir um PR que muda se você estiver ok). Nem tenho certeza se ter objetos ajudaria muito: se você olhar para o meu ex, tem uma dependência de um provedor para o outro.

Obrigado por suas respostas, encerrando o problema.

@vicb , terei prazer em mesclar um PR relacionado à documentação para isso.

Posso estar um pouco atrasado para a cena, mas o que acontece quando tenho dependências que preciso inicializar é abandonar a anotação @dataProvider e usar yield lugar.

@srosato Estou sendo burro (não usei yield). Você poderia dar um breve exemplo de como fazer isso, por favor? Via https://gist.github.com se um exemplo aqui não for apropriado.

Se o seu @dataProvider requer um código de configuração e eles não são muito grandes, você pode especificá-los como strings e eval no teste. Por exemplo:

public function myProvider() {
    return [
        'new Klass("param 1", "param 2")',
        'new Klass("param 1", "param 2")',
    ];
}

/**
 * <strong i="8">@dataProvider</strong> myProvider
 */
public testMyFunction($instance_str) {
    $klass = eval("return {$instance_str};");
    # continue testing $klass ...
}

meus dois centavos:

Em vez de um provedor de dados contando com uma propriedade estática (que sabemos que não é possível), estou definindo os dados / objetos necessários como membro (s) da classe de teste no método setUp() .
em tearDown() i defini-los como nulo, ou quando houver objetos complexos, implemento um método clear() para eles.

contanto que não haja muitas dependências em toda a classe de teste, me sinto confortável com essa abordagem.
mas quando há mais de uma ou duas dessas dependências, você pode precisar repensar seu design geral.

especificamente, eu uso uma conexão de banco de dados como propriedade estática que é definida em setUpBeforeClass() e defina $queryBuilder , que obtém a conexão injetada, como um membro de classe em setUp() (em vez de retornando em um provedor de dados).

Uma solução seria definir um novo método privado e definir quantas variáveis ​​forem necessárias, como static uns dentro dele. Em seguida, verifique se um deles está definido e, se não estiver, defina todos e retorne (ou retorne). Desta forma, _as variáveis ​​serão declaradas apenas uma vez_, mesmo se você tiver dez provedores ou qualquer outro. Além disso, você pode usá-lo em setUpBeforeClass() ou setUp() .

Veja em um exemplo:

private static function getData()
{
    static $data, $anotherData;

    if (!isset($data)) {
        $data = new TestClass();
        $anotherData = [];
    }

    return [
        $data,
        // Or: clone $data
        $anotherData,
    ];
}

public static function setUpBeforeClass()
{
    list(self::$sampleJson, self::$sampleData) = self::getData(); 
}

public function sampleProvider()
{
    $data = self::getData();

    return [
        $data
    ];
}

@MAChitgarha graças ao seu comentário ontem eu descobri sobre este ticket. :)

Estou trabalhando na refatoração da lógica do provedor de dados no número 3736, que resolverá alguns dos problemas comuns:

  • não tem mais esse grande pico de atividade no início da execução, pois todos os provedores de dados são carregados
  • os provedores de dados podem ser executados após os acessórios setUpBeforeClass e setUp
  • provedores de dados não estáticos tornam-se possíveis
  • geradores serão muito mais eficientes

@epdenouden Feliz por ouvir isso! ;) O segundo item da sua lista é bom. Esperando por isso.

Provedores de dados não estáticos tornam-se possíveis

Não é possível atualmente? Eu sempre uso provedores de dados não estáticos em meus testes em vez de estáticos, e eles funcionam como esperado sem problemas (usando PHPUnit 7.5.6). Estou errado?

Não é possível atualmente? Eu sempre uso provedores de dados não estáticos em meus testes em vez de estáticos, e eles funcionam como esperado sem problemas (usando PHPUnit 7.5.6). Estou errado?

Não, você está correto! Minha frase não foi precisa o suficiente, obrigado por trazer isso à tona. No fundo do código de manipulação do provedor de dados, ele atualmente lê:

private static function getDataFromDataProviderAnnotation($allTheParameters): ?iterable
    // code for locating the data provider
    // [...]
                if ($dataProviderMethod->isStatic()) {
                    $object = null;
                } else {
                    $object = $dataProviderClass->newInstance();
                }

    // code for preparing returned data rows
    // [...]
}

Então aqui está o segredinho sujo:

  • você pode usar provedores de dados não estáticos e
  • eles serão chamados em um instanceof real, mas ...
  • _não é a mesma instância_ que é usada para os acessórios e testes

Você me deu algo para pensar e validar em relação à refatoração do provedor de dados! Preciso adicionar testes extras para ter certeza de que as instâncias do objeto são as esperadas, não apenas do mesmo tipo ou clone. ☕️ e 🍰 comigo quando você estiver em Amsterdã.

@epdenouden Mas eu não recebo nenhuma falha! No PHPUnit 7.5.13, a asserção não gera nenhum erro. Como você consegue esse erro? Incompatibilidade com versões anteriores, você quer dizer?

Este é o branch de provedor de dados de carregamento lento em que estou trabalhando, que se baseia no 8.2. Vou verificar 7.5.x a seguir, obrigado pelo aviso.

Se isso funcionar, terei que voltar a alguns outros problemas mais antigos que solicitavam explicitamente a implementação de métodos de provedor não estáticos e examinar os casos de uso exatos novamente.

Em qualquer caso: o seu comentário já inspirou um teste muito útil :)

E @MAChitgarha sim, isso seria uma pausa BC

@epdenouden vou esperar por isso. Por alguns motivos, em meu projeto, estou usando o PHPUnit 7.5.13; mas irei atualizá-lo para 8.2. * em breve. E boas notícias, se a quebra do BC puder ser consertada. Não conheço a estrutura do PHPUnit em si, então não sei o que você mudou, mas acho que é possível consertar. No entanto, é tudo com você! :)

E boas notícias, se a quebra do BC puder ser consertada. Não conheço a estrutura do PHPUnit em si, então não sei o que você mudou, mas acho que é possível consertar. No entanto, é tudo com você! :)

Introduzir uma quebra de compatibilidade com versões anteriores sem um bom motivo não é algo que @sebastianbergmann permitiria, portanto, será corrigido antes de ir para a próxima versão. PHPUnit é uma ferramenta usada para proteger a qualidade do software na comunidade PHP e não no meu playground pessoal.

Então, sim, diga-nos o que você gostaria de ver como um usuário final / desenvolvedor de testes. Apenas tenha em mente que projetos como esses não têm um grande grupo de desenvolvedores voluntários.

Curiosidade: o teste acima realmente funciona em ambas as versões 7.xe 8.xe falha no protótipo JIT.

O código no qual estou trabalhando está muito mais parecido com o fluxo do código original. Portanto, pode ser o resultado de um bug, alguma lógica que ainda preciso refatorar mais, algo de baixo nível sobre objetos e reflexão em PHP que preciso pesquisar ou talvez apenas alguns testes ingênuos de minha parte. 🔬

@epdenouden JIT? Você está testando com JIT para PHP ou há algo que não sei? Desculpe, estou um pouco confuso; você está falando sobre PHP7.x e PHP8.x ou PHPUnit7.x e PHPUnit8.x ?!

@epdenouden JIT? Você está testando com JIT para PHP ou há algo que não sei? Desculpe, estou um pouco confuso; você está falando sobre PHP7.x e PHP8.x ou PHPUnit7.x e PHPUnit8.x ?!

@MAChitgarha Eu entendo sua confusão porque o PHP8 está recebendo um _compiler_ just-in-time. Eu estava usando o termo para a implementação do novo

Isso funcionará bem com o próximo JIT do PHP versão 8, pois não usa nenhum recurso complicado da própria linguagem PHP. Todo o meu trabalho está profundamente dentro do PHPUnit, refazendo coisas como análise de configuração, carregamento e execução de teste, etc.

Se parece que não estou fazendo nada, estou fazendo certo. Exceto para aquela fatura reduzida do GCP ou AWS. 💸

Esta página foi útil?
0 / 5 - 0 avaliações