Junit4: Regra ExpectedException não adulterada

Criado em 5 mai. 2019  ·  22Comentários  ·  Fonte: junit-team/junit4

Depois de atualizar o Spring Framework para JUnit 4.13 beta 3, percebi que org.junit.rules.ExpectedException agora é @Deprecated , e essa mudança gera muitos avisos em uma grande base de código como essa.

Como o 4.13 é o último lançamento pretendido na linha 4.x, não acho que faça sentido descontinuar um recurso tão comumente usado e com suporte.

Se você mantiver a depreciação em vigor, temo que isso irá incomodar apenas milhares (milhões?) De desenvolvedores sem um bom motivo.

rules

Comentários muito úteis

@daicoden

Tenho certeza de que assertThrows retorna o que pode ser jogado, então você pode simplesmente capturá-lo e fazer afirmações como faria para qualquer outro objeto:

StatusRuntimeException thrown = assertThrows(StatusRuntimeException.class, () -> {
   GrpcDispatch.makeRequest(service::enableJob, JobReference.newBuilder().setName(UNKNOWN_JOB).build());
});

assertEquals(Status.INVALID_ARGUMENT, thrown.getStatus());

Todos 22 comentários

Acho que o aviso de suspensão de uso é uma boa maneira de indicar que algo melhor está disponível, portanto, os avisos não estão lá sem um bom motivo. ExpectedException pode ser facilmente mal utilizado, mas ainda assim é recomendado em muitos lugares.

A questão é o quão problemático é para nossos usuários receber esses avisos de suspensão de uso. Na minha empresa, os avisos de depreciação não aparecem no momento da compilação (apenas ao visualizar o código).

Acho que Sam tem razão aqui. Já vi muitos builds que proíbem todos os (novos) avisos do compilador. Esses projetos teriam que passar por todas as classes de teste que usam ExpectedException e adicionar uma anotação SuppressWarnings .

@stefanbirkner WDYT?

Parece razoável fazer com que os clientes suprimam os avisos. Eles também podem suprimir todos os avisos de depreciação; tratar os avisos de suspensão de uso como erros impossibilita o uso de qualquer coisa.

A suspensão de uso é nossa melhor maneira de indicar às pessoas que a API antiga é problemática e que APIs muito melhores estão disponíveis.

Eu entendo que os desenvolvedores podem suprimir avisos localmente dentro de um IDE; no entanto, discordo que seja uma boa ideia descontinuar um recurso principal na versão final (planejada) do JUnit 4.x.

IMHO, não é uma boa ideia descontinuar um recurso central na versão final de qualquer framework, especialmente um de uso tão difundido como JUnit 4.

Como um exemplo concreto, no conjunto de testes principal do Spring Framework, agora temos milhares de avisos de depreciação para o uso da regra ExpectedException .

As únicas opções que temos são:

  1. Suprime os avisos de depreciação em cada classe de teste que usa ExpectedException .
  2. Evite o uso de ExpectedException em cada classe de teste que o utiliza.
  3. Fazer nada.

Se seguirmos com o nº 1, provavelmente poderemos conseguir isso com algum script que suprime os avisos no nível da classe, mas isso suprimiria os avisos de depreciação de outras APIs que provavelmente queremos / precisamos saber e não ignorar. Caso contrário, teremos que passar manualmente por cada classe de teste e descontinuar cada uso da API ExpectedException , e isso inclui cada interação única com a instância de regra em todos os métodos de teste afetados.

Se escolhermos o número 2, essa é uma grande tarefa que pode levar vários dias para ser realizada manualmente. A automação parcial pode ser possível, mas o investimento na automação (scripts, etc.) pode ser bastante extenso por si só.

Se seguirmos com o número 3, teremos avisos de depreciação desnecessários (de nossa perspectiva) que obscurecem nossa visão dos avisos de depreciação com os quais realmente nos importamos, e isso leva ao efeito Janela Quebrada , que queremos evitar.

Fazer o nº 2 simplificaria a migração futura para o JUnit 5.

Fazer o nº 2 simplificaria a migração futura para o JUnit 5.

Certo. Isso tornaria uma migração para JUnit Jupiter ou AssertJ potencialmente mais fácil; entretanto, nem todo mundo deseja ou precisa migrar os conjuntos de testes existentes.

Portanto, não considero esse um argumento válido.

Não tenho certeza de por que esta provavelmente a última versão do JUnit 4.x está relacionada ao fato de termos ou não obsoleto. JUnit raramente exclui _qualquer_ APIs, então a depreciação não é uma indicação de que uma API está indo embora. É uma indicação de que a API pode não ser suportada e / ou existem APIs melhores. Ambos são verdadeiros neste caso (rejeitamos as alterações propostas a esta regra porque achamos que é melhor apenas permitir que as pessoas usem assertThrows() )

Eu sugiro que o Spring migre lentamente para longe de ExpectedException . Alguns dias de trabalho ao longo de alguns meses para chegar a testes mais seguros e fáceis de entender parece uma vitória. A vantagem é que outras pessoas menos familiarizadas com as mudanças recentes no JUnit saberão evitar ExpectedException

ErrorProne tem uma verificação para isso que fornecerá uma correção recomendada, portanto, não acho que demoraria vários dias para resolver esse problema.

TL; DR Sou a favor da suspensão de ExpectedException.

@kcooney já disse que a depreciação no JUnit 4 não significa que removeremos coisas porque uma decisão de design do JUnit 4 é para evitar alterações significativas. Portanto, não é tão importante se esta é a última versão 4.x ou não.

Eu acho que é útil ter a suspensão de uso porque diz às pessoas que existe uma maneira melhor de verificar as exceções. Trabalhei para algumas empresas nos últimos 10 anos e sempre há pessoas que se surpreendem por haver algo melhor do que @Test(expected=ABeautifulException.class . A suspensão de uso é valiosa para eles porque geralmente dão uma olhada quando vêem o código riscado no IDE.

Os avisos de depreciação podem ser irritantes porque não há necessidade de corrigir esses avisos. A origem disso é a maneira como o JUnit 4 usa a depreciação. Portanto, acho que não há nenhuma correção para isso além de não descontinuar ExpectedException (e, finalmente, não usar a depreciação).

Por último, mas não menos importante, existem empresas cujas compilações quebram em caso de avisos de depreciação e, portanto, não podem atualizar facilmente para o JUnit 4.13. Não tenho certeza de quantas dessas empresas existem, que são empresas que a) têm essa política de construção eb) querem fazer upgrade para o JUnit 4.13.

@sdeleuze , @ mp911de e @rweisleder você votou por reverter a

Só por um pouco mais de informação, que demorou cerca de 1,5 dias para migrar base de código do Spring Framework de JUnit de ExpectedException a de AssertJ assertThatExceptionOfType .

Uma pequena sugestão que pode ajudar é considerar a suspensão do uso apenas do método ExpectedException.none() vez de toda a classe. Isso ainda daria o aviso às pessoas, mas tornaria muito mais fácil suprimir isoladamente.

ExpectedException JUnit é usado como um tipo primitivo em testes que realizam asserções de exceção mais extensas do que apenas @Test(expected = …) . Esses são normalmente testes que dependem de Hamcrest ou asserções integradas e não tanto AssertJ ou algo parecido.

Ter o JUnit 4.13 é um sinal de que a manutenção ativa está acontecendo. As bases de código que permanecem na API JUnit 4.x provavelmente não serão migradas para a API JUnit 5 em breve, mas, em vez disso, mantêm a API com a potencial migração para o mecanismo Vintage. Ter uma classe repentinamente obsoleta gerou novos avisos e esforço adicional para migrar para um utilitário de asserção diferente, embora o código ainda possa estar funcionando.

Observei algumas vezes a depreciação e a introdução de API aprimorada. Na maioria dos casos, a depreciação ajudou os mantenedores e não os usuários reais. Do lado do usuário, isso gerou principalmente esforço sem nenhum benefício.

Uma pequena sugestão que pode ajudar é considerar a suspensão do uso apenas do método ExpectedException.none() vez de toda a classe. Isso ainda daria o aviso às pessoas, mas tornaria muito mais fácil suprimir isoladamente.

Se a equipe da JUnit 4 decidir não descumprir a regra ExpectedException , acho que esse seria um bom compromisso.

Eu me pergunto se @Test(expected = ...) também deveria ser descontinuado, já que agora temos assertThrows . Ou pelo menos a referência a ExpectedException deve ser removida do Javadoc do atributo.

Eu me pergunto se @Test(expected = ...) também deveria ser descontinuado, já que agora temos assertThrows . Ou pelo menos a referência a ExpectedException deve ser removida do Javadoc do atributo.

Sim, o atributo expected em org.junit.Test provavelmente deve ser descontinuado. Em qualquer caso, o Javadoc de nível de classe para org.junit.Test e o atributo expected devem ser atualizados para recomendar o uso de assertThrows .

Eu seria a favor de suspender apenas ExpectedException.none() .

Acredito que usar a suspensão de uso para anunciar um novo recurso é uma abordagem pesada que prejudicaria a confiança no processo de desenvolvimento do JUnit e impediria a adoção da versão 4.13.

Deve haver uma sobreposição de versões quando o antigo e o novo recurso coexistem sem que nenhum deles seja descontinuado. Como outros apontaram, ter dependências de métodos obsoletos é uma proibição para muitos projetos que mantêm a qualidade do código alta. Isso significa que a atualização do JUnit deve estar no mesmo commit que as alterações para migrar a base de código inteira.

Acredito que a única situação em que um recurso é descontinuado e a substituição é adicionada na mesma versão é quando o recurso está fundamentalmente quebrado e deve ser migrado imediatamente. Não acho que ExpectedException esteja fundamentalmente quebrado.

Eu costumava verificar as dependências mais recentes para ver quais mudanças estão por vir e como posso preparar meu código. Então, como faço para preparar meu código para a migração JUnit 4.13? Eu sei que ExpectedException será descontinuado, mas ainda não tenho um bom substituto. Eu precisaria implementar meu próprio código para capturar e verificar exceções para evitar essa bagunça completamente.

Só porque o 4.13 está planejado para ser o lançamento final do 4.x, não é um bom motivo para abandonar as práticas de desenvolvimento de software sãs. Na verdade, o 4.13 pode ser lançado sem a depreciação de ExpectedException e o 4.14 pode ser lançado no dia seguinte com a descontinuação. Isso passaria a mensagem ao mesmo tempo que daria aos desenvolvedores um tempo para fazer a migração sem a pressão indevida.

Deve haver uma sobreposição de versões quando o antigo e o novo recurso coexistem sem que nenhum deles seja descontinuado.

Porque? A intenção da suspensão é conscientizar as pessoas sobre a nova API, em nossa opinião melhor.

Também concordo que devemos descontinuar ExpectedException.none() para torná-lo menos invasivo.

@stefanbirkner Ok com você?

@marcphilipp Quando eu atualizo meu código, quero que as coisas funcionem sem depreciações, a menos que eu esteja fazendo algo errado. É muito intrusivo descontinuar um recurso e fornecer uma substituição ao mesmo tempo. Em vez disso, as notas da versão 4.13 devem dizer que ExpectedException será descontinuado na próxima versão e sugerir uma substituição. Dessa forma, terei uma maneira de migrar meu código passo a passo sem ter que permitir suspensões nesse meio tempo. Isso é apenas uma cortesia básica para os usuários que usam o JUnit 4.12 há anos e não querem grandes mudanças.

IMHO o ponto de um aviso de depreciação _is_ para dar-lhe tempo para migrar seu código para a substituição. Se precisar contornar o aviso de depreciação, você pode criar seu próprio método de fábrica temporário que delega para ExpectedException.none() .

OK, o PR atenua o problema até certo ponto, é definitivamente um passo na direção certa. Obrigado por fazer isso!

Em vez de sugerir que os usuários implementem um método de fábrica, o código poderia fornecer seu próprio método não obsoleto (talvez none com um argumento extra).

Ainda não entendo a resistência em dar aos usuários uma versão transitória. Ao implementar uma mudança incompatível, é muito comum fazer uma versão que permita a transição sem forçá-la de forma alguma.

Sem uma versão de transição, os usuários tentariam 4.13, veriam toneladas de avisos e voltariam para 4.12.

Com uma versão transicional, os usuários tentariam 4.14, veriam toneladas de avisos, tentariam 4.13, não veriam avisos, usariam. Alguns atualizariam para o 4.14 rapidamente, outros ficariam felizes com o 4.13, a menos que tenham um bom motivo para atualizar. Deve estar bem de qualquer maneira.

Também não entendo por que essa suspensão de uso deve ter acontecido no JUnit 4. Estou migrando para o JUnit 5 usando junit-vintage-engine para oferecer suporte temporário às duas versões. Mas isso puxa o JUnit 4.13 que, por algum motivo, decidiu que era uma boa ideia descontinuar coisas (que têm suporte há anos) no que é provavelmente um de seus últimos lançamentos.

Também parece não haver razão para a reprovação além de apontar uma alternativa - isso poderia ter sido conseguido pelo menos oferecendo uma alternativa (tornar o construtor público, ou oferecer outro método estático - mensagem comunicada).

O código ExpectedException como está agora continuará funcionando (e continuará funcionando mesmo em versões posteriores), pois ele simplesmente usa recursos básicos do JUnit oferecidos de uma maneira (na época) conveniente.

Qual é o substituto para afirmar propriedades sobre a exceção ...

código antigo:

thrown.expect(StatusRuntimeException.class);
thrown.expect(its(StatusRuntimeException::getStatus, toBe(Status.INVALID_ARGUMENT)));
GrpcDispatch.makeRequest(service::enableJob, JobReference.newBuilder().setName(UNKNOWN_JOB).build());

Parece que está faltando uma versão de correspondência para assertThrows para permitir que a exceção esperada seja totalmente obsoleta ... Eu sei que é tarde demais, mas ninguém mais tem esse problema?

@daicoden

Tenho certeza de que assertThrows retorna o que pode ser jogado, então você pode simplesmente capturá-lo e fazer afirmações como faria para qualquer outro objeto:

StatusRuntimeException thrown = assertThrows(StatusRuntimeException.class, () -> {
   GrpcDispatch.makeRequest(service::enableJob, JobReference.newBuilder().setName(UNKNOWN_JOB).build());
});

assertEquals(Status.INVALID_ARGUMENT, thrown.getStatus());
Esta página foi útil?
0 / 5 - 0 avaliações