Junit4: Adicionar anotações de execução parametrizada antes e depois

Criado em 16 nov. 2009  ·  39Comentários  ·  Fonte: junit-team/junit4

Atualmente, não há uma maneira simples de invocar um método antes ou depois de uma instância parametrizada ser executada.

Dito de outra forma, junto com @BeforeClass e @AfterClass deve haver @BeforeParameterRun @AfterParameterRun. Cada um é acionado antes/depois que uma instância parametrizada é executada.

feature parameterized

Comentários muito úteis

pessoal, "depois/antes" para cada execução parametrizada seria muito útil.

Todos 39 comentários

Que tal um parâmetro na própria anotação @Before : @Before(true) ou @Before(onEachParameter = true). O padrão é falso, é claro.

O objetivo dessa solicitação é ter um local para colocar o código que é executado antes e depois da execução de um parâmetro. Não antes de cada teste em um determinado parâmetro executado. Assim, a ordem seria BP BT T1p1 AT BT T2p1 AT BT T3p1 AT AP BP BT T1p2 AT BT T2p2 AT ... onde

  • BP é o método antes da execução do parâmetro @BeforeParameterRun
  • BT é o método de teste antes @Before
  • Txpy é o teste x, no parâmetro y @Test , por exemplo, T2P3 é o teste 2 no parâmetro 3
  • AT é o método de teste após @After
  • AP é o parâmetro após a execução @AfterParameterRun

Eu implementei isso no meu github, mas tive que desligá-lo devido a problemas com meu empregador. Eu dei um puxão para David Saff e ele o incluiu em sua filial, mas foi retirado mais tarde. Vou procurar o número da revisão onde o código foi incluído após esta mensagem.

Parece que o merge foi para uma árvore do dsaff por volta de 3 de abril de 2009. Nos comentários você pode ver um merge do bigmikef. Está desatualizado, mas funcionou na época.

pessoal, "depois/antes" para cada execução parametrizada seria muito útil.

Isso pode ser feito usando regras, em particular ExternalResource. Os métodos implementados são chamados para cada parâmetro:

@RunWith(Parameterized.class)
public class ParameterRuleTest {
    @Parameters()
    public static Iterable<Object[]> data() {
        return Arrays.asList(new Object[]{}, new Object[]{});
    }

    <strong i="6">@Rule</strong>
    public ExternalResource externalResource = new ExternalResource() {
        protected void before() throws Throwable { System.out.println("before"); }
        protected void after() { System.out.println("after"); }
    };

    <strong i="7">@Test</strong> public void myTest1() { System.out.println("test"); }
}

Isso produz

test
after
before
test
after

Se isso for aceitável, feche este problema.

@matthewfarwell Posso estar entendendo mal sua resposta, mas no exemplo que você forneceu, essa regra seria executada antes e depois de cada teste para cada instância criada pelo executor parametrizado.

O que eu acho que @bigmikef queria era a capacidade de poder executar um método antes e depois uma vez para cada instância criada pelo executor parametrizado. Não acho que @ClassRule possa ser usado neste caso porque é executado em um contexto estático e é executado apenas uma vez para todas as instâncias parametrizadas.

Eu não me importaria de tomar a iniciativa de implementar isso, pois seria benéfico no meu projeto atual. Minha proposta segue abaixo:

Resumo da Implementação:

A arquitetura tradicional do JUnit é uma instância de uma classe Java por método de teste. Para implementar os requisitos acima, acredito que o runner Parameterized precisa ser modificado para criar uma instância Java para cada instância parametrizada.

A principal razão para isso é que a lógica executada antes e depois de cada execução parametrizada é local para a classe e não colocada em algum escopo global com um modificador estático. No meu caso de uso específico, configurei e desmontei o que exigiria o uso de parâmetros exclusivos para cada instância parametrizada.

Adições / Modificações:

org.junit.runners.Parameterized

  • Adicione uma nova anotação chamada ParameterRule .
  • Adicione uma nova classe interna protegida que estenda TestClassRunnerForParameters chamada algo como SingleInstanceRunnerForParameters .
  • Instancie TestClassRunnerForParameters ou SingleInstanceRunnerForParameters dependendo se ParameterRule existe ou não.

org.junit.runners.BlockJUnit4ClassRunner

  • Mova a criação do objeto de teste de methodBlock . Em vez disso, forneça o objeto de teste como um parâmetro.

    • A principal razão para isso é que as classes estendidas podem passar sua própria instância para methodBlock. Caso contrário, se alguém quiser estender BlockJUnit4ClassRunner e manter a maior parte da funcionalidade, deve copiar os seguintes métodos: withRules , withMethodRules , withTestRules e getMethodRules

  • Se mover a criação do objeto de teste de methodBlock não for aceitável, alterar os seguintes métodos de private para protected reduziria a duplicação de código:

    • withRules

    • withMethodRules

    • withTestRules

    • getMethodRules

org.junit.runners.Parameterized.SingleInstanceRunnerForParameters

  • Adicione um método chamado withParameterRules que executaria regras anotadas por ParameterRule .
  • Execute withParameterRules em uma versão substituída de classBlock

Se houver preferência em fornecer um exemplo dessa implementação, posso no meu fork, mas queria discutir o design antes de investir uma quantidade significativa de tempo.

@coreyjv o que há de errado com a resposta de @matthewfarwell ?

@Tibor17 , veja minha resposta à sua resposta: https://github.com/KentBeck/junit/issues/45#issuecomment -11293228

Sua implementação é executada antes e depois de cada método de teste. No meu caso, minha configuração e desmontagem são significativamente caras, e é por isso que quero executá-la antes e depois de cada execução parametrizada.

Por exemplo, se eu tiver uma classe de teste que, quando executada uma vez com uma configuração comum (localizada em uma @ClassRule atualmente), a classe inteira executa e conclui os testes em ~ 20 segundos. No entanto, se eu mover a configuração para uma regra de nível de método, levará aproximadamente 240 segundos para executar e concluir. Isso é apenas para uma aula. Na minha situação esse aumento no tempo não é aceitável.

Teoricamente, eu poderia colocar todos os meus testes em um único método para emular o comportamento necessário, mas não acho que seja um design de teste sustentável.

@Tibor17 , não tenho certeza se as notificações saem nas atualizações, então pensei em outro comentário.

@dsaff , alguma opinião sobre esta proposta?

@coreyjv
Basicamente, o que você quer é algo como @BeforeParams e @AfterParams executados apenas uma vez no ciclo de vida. Estou certo?

@Tibor17 , essencialmente sim. Acho que usaria uma única anotação que permitisse a execução de regras.

Se eu usasse o BeforeClass (BC) e o AC, ficaria assim?
BC BP BT T1p1 AT BT T2p1 AT BT T3p1 AT AP BP BT T1p2 AT BT T2p2 AT AC

@ Tibor17 , sim, acredito que o que você apresentou é preciso.

@coreyjv
Acho que esse recurso que você quer pode ser interessante. Qual é o seu caso de uso real?

@Tibor17
Atualmente, estou trabalhando na construção de um conjunto de testes de navegador da Web usando o Selenium para navegar e inspecionar o conteúdo da página que é alimentado ao JUnit para execução do caso de teste.

Minhas necessidades resultaram em uma classe de teste JUnit por página para testar coisas como:

  • Existe um campo?
  • É somente leitura?
  • O texto da etiqueta é "X"?

Essas páginas podem estar em qualquer profundidade arbitrária dentro do aplicativo, então coloco a lógica de navegação para chegar à página de destino em uma regra "antes" (atualmente @ClassRule). Idealmente, eu gostaria de executar o mesmo teste com parâmetros diferentes, que são lidos e usados ​​na "configuração". Esses parâmetros seriam itens como identificador de usuário e credenciais de senha e outros detalhes relevantes para chegar à página de destino.

Vou citar uma resposta anterior que escrevi sobre por que quero fazer isso por "ciclo de vida":

Por exemplo, se eu tiver uma classe de teste que, quando executada uma vez com uma configuração comum (localizada em uma @ClassRule atualmente), a classe inteira executa e conclui os testes em ~ 20 segundos. No entanto, se eu mover a configuração para uma regra de nível de método, levará aproximadamente 240 segundos para executar e concluir. Isso é apenas para uma aula. Na minha situação esse aumento no tempo não é aceitável.

@coreyjv
significa que os parâmetros também devem estar visíveis na configuração recém-anotada.
isso dá uma visão geral das necessidades.
Prossiga.

@Tibor17 , corrija os parâmetros que devem estar visíveis na configuração. Que tipo de esclarecimento ou detalhes adicionais você gostaria que eu fornecesse?

Eu acho que a visão geral e meus comentários anteriores fornecem uma boa imagem da minha necessidade (você pode ver a lista de discussão também) sem entrar nas especificidades do aplicativo que eu acho que agregaria valor limitado.

@coreyjv
Talvez você deva agora empurrar um puxão e incluir lá as pessoas nesta edição também.

@Tibor17
Tudo bem para mim, eu estava apenas procurando uma aprovação inicial do "design" antes de prosseguir.

@coreyjv
Estou ansioso para ver algum teste que descreva o uso, mesmo que o impl do runner esteja incompleto.
O que os outros diriam sobre o teste...

@Tibor17

O que os outros diriam sobre o teste...

Você está me procurando para postar uma aula de teste de amostra? É realmente muito chato, pois é como qualquer outro teste parametrizado (onde os parâmetros são lidos de um banco de dados, por exemplo) e a configuração do "ciclo de vida" seria usada no lugar da configuração @ClassRule ou @Rule . Como mencionei anteriormente, um caso simples seria a configuração que usasse diferentes combinações de nomes de usuário, senhas e funções de aplicativo.

Não acho que seja tão fora do comum. É apenas neste caso que a configuração é proibitivamente cara para fazer antes e depois de cada método.

@coreyjv

Que tal fazer isso no seu teste

@BeforeParameter
public void static setup() { // flag this call in some variable and assert it's set }

@Tibor17
Como mencionei anteriormente, essa configuração precisa ser parametrizada e seria exclusiva para cada conjunto de parâmetros retornados por @Parameters , portanto, não acho que essa abordagem funcione.

@coreyjv
Eu acho que você já tem algum código de instantâneo em sua mente.
Você ainda quer discutir isso aqui ou quer abrir um pull?

@Tibor17
Vou montar e enviar um pull request. Posso não fazer isso até depois do Ano Novo, mas vou ver o que posso fazer. Obrigado pela discussão eu aprecio isso.

Pensamentos:

  1. Devemos fazer isso acontecer, e estou feliz em trabalhar com as partes interessadas.
  2. Não podemos alterar a assinatura de nenhum método protegido. Mas extrair um methodBlock protegido (método FrameworkMethod, teste de objeto) parece uma ideia muito boa.
  3. Ao escrever a interface ParameterRule, tenha em mente que acho que precisamos melhorar o funcionamento de Rule e ClassRule. https://docs.google.com/document/d/1B6Z45BSGsY08rZqe2KUZTJ3RVsnUE1-HcXjl5MFm9ls/edit ajuda a explicar onde eu gostaria de ver as coisas avançando: ParameterRule poderia se beneficiar especialmente desse tipo de abordagem de fábrica de regras, pois permitiria uma fácil reutilização de coisas como TemporaryFolder.
  4. Acho que não podemos alterar unilateralmente o padrão de instanciação de Parameterized, porque os testes atuais podem depender do comportamento de uma instância por teste. Como podemos contornar isso?

uma. Adicione uma anotação ou parâmetro de anotação para habilitar o comportamento diferente.
b. Crie uma subclasse de Parameterized com o comportamento diferente.
c. Outro pensamento seria criar uma nova instância da classe de teste para cada teste, mas passar a essa instância o mesmo array de objeto de parâmetro, que poderia ser operado por uma regra de parâmetro. Isso provavelmente seria mais recompensador se os parâmetros fossem agrupados em um único objeto. Uma ideia potencialmente maluca seria anexar a ParameterRule à própria classe de parâmetro...

Acho que prefiro c a b, e b a a, mas aberto à discussão.

@dsaff

Devemos fazer isso acontecer, e estou feliz em trabalhar com as partes interessadas.

Obrigado!

Não podemos alterar a assinatura de nenhum método protegido. Mas extrair um methodBlock protegido (método FrameworkMethod, teste de objeto) parece uma ideia muito boa.

Implementei meus requisitos esta manhã e acabei não modificando essa assinatura de método (fui com a abordagem de alterar certos métodos internos de private --> protected).

Ao escrever a interface ParameterRule, tenha em mente que acho que precisamos melhorar o funcionamento de Rule e ClassRule. https://docs.google.com/document/d/1B6Z45BSGsY08rZqe2KUZTJ3RVsnUE1-HcXjl5MFm9ls/edit ajuda a explicar onde eu gostaria de ver as coisas avançando: ParameterRule poderia se beneficiar especialmente desse tipo de abordagem de fábrica de regras, pois permitiria uma fácil reutilização de coisas como TemporaryFolder.

Vou dar uma olhada nisso e ter em mente.

Acho que não podemos alterar unilateralmente o padrão de instanciação de Parameterized, porque os testes atuais podem depender do comportamento de uma instância por teste. Como podemos contornar isso?

Minha implementação está condicionada à existência de uma anotação @ParameterRule . Caso detecte que um método ou campo está anotado com esta anotação ele irá alterar o padrão de instanciação de Parametrizado.

uma. Adicione uma anotação ou parâmetro de anotação para habilitar o comportamento diferente.

Veja acima.

b. Crie uma subclasse de Parameterized com o comportamento diferente.

Isso definitivamente poderia ser implementado como uma subclasse e eu ficaria bem com isso também. Meu processo de pensamento foi trabalhar com a classe existente porque essa parecia ser a intenção do criador.

c. Outro pensamento seria criar uma nova instância da classe de teste para cada teste, mas passar a essa instância o mesmo array de objeto de parâmetro, que poderia ser operado por uma regra de parâmetro. Isso provavelmente seria mais recompensador se os parâmetros fossem agrupados em um único objeto. Uma ideia potencialmente maluca seria anexar a ParameterRule à própria classe de parâmetro...

Não tenho certeza se sigo completamente, mas ainda posso atender ao requisito de ter uma configuração comum que é executada uma vez por matriz de parâmetros retornados do método @Parameters ?

Você prefere que eu envie uma solicitação pull para que possamos discutir o código real?

@coreyjv , sim, acho que se você tem código que gostaria de compartilhar, é um ótimo próximo passo. Obrigado!

@dsaff

Parece bom, vou tentar fazer um pull request nos próximos dias.

@dsaff
@coreyjv

Já houve alguma resolução para este problema? Temos alguns testes Junit que usam a opção de parâmetro para especificar navegadores diferentes, como IE/Firefox/Chrome, e estava lançando um novo navegador para cada teste, mas para aumentar a velocidade de execução dos testes eu estava tentando mudar para inicial e parando os navegadores em torno de uma classe de testes. Se eu usar o beforeClass e afterClass, há muito tempo entre um dos parâmetros terminar e fechar o navegador, então o tempo limite do driver remoto da web. Eu gostaria de usar essa abordagem AfterParameterRun, mas vejo muita discussão e esse problema leva a algumas solicitações de pull, mas parece que nada foi mesclado no main?
Obrigado,
Bradley

Eu também tenho um caso em que gostaria de ver uma função 'AfterParameter'.

@coreyjv Essa funcionalidade está no pipeline porque estamos executando casos de teste parametrizados onde precisamos dessas anotações. Ambos @BeforeParam e @AfterParam são obrigatórios.

@bileshg --

De acordo com @dsaff, esse pedido foi 'desativado' há alguns anos. Digo a ele se ele quiser ressurgir novamente. Além disso, não tenho certeza se isso é algo que seria mais apropriado para o JUnit 5 agora (não acompanhei o progresso lá).

@coreyjv Ok. Obrigado por responder. Na verdade, eu precisava disso para outra coisa - Obter contagem de casos de teste com falha de acordo com o parâmetro. Dê uma olhada nesta pergunta .

Vejo que "@BeforeParam/ @AfterParam for Parameterized runner #1435" foi mesclado e adicionado às notas de versão 4.13. Isso significa que haverá uma versão 4.13? Quando? Estou precisando muito dessa funcionalidade! Eu não vi isso no JUnit 5, então estou me perguntando quando posso esperar que @BeforeParam/ @AfteParam esteja disponível.

@dstaraster atualmente a maior parte da equipe JUnit está se concentrando no lançamento do JUnit 5.0. Espero que logo depois disso possamos começar a versão 4.13.

@kcooney Também estou muito interessado nessas anotações @BeforeParam e @AfterParam (e infelizmente ainda não estou pronto para migrar meus milhares de testes para JUnit5).
A versão 4.13 ainda é esperada?

@raubel Abri o nº 1496 para discutir uma versão 4.13.

O JUnit5 pode executar testes no estilo JUnit4, portanto, você não precisa migrar todos os seus testes para usar os recursos do JUnit5.

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

Questões relacionadas

lvc picture lvc  ·  6Comentários

gonzen picture gonzen  ·  7Comentários

diogoeag picture diogoeag  ·  35Comentários

gitIvanB picture gitIvanB  ·  9Comentários

sabi0 picture sabi0  ·  13Comentários