Este é o código:
package com.XXX;
public final class Foo {
private Foo() {
// intentionally empty
}
public static int bar() {
return 1;
}
}
Este é o teste:
package com.XXX;
public FooTest {
@Test
void testValidatesThatBarWorks() {
int result = Foo.bar();
assertEquals(1, result);
}
@Test(expected = java.lang.IllegalAccessException.class)
void testValidatesThatClassFooIsNotInstantiable() {
Class cls = Class.forName("com.XXX.Foo");
cls.newInstance(); // exception here
}
}
Funciona bem, a classe é testada. Mas Cobertura diz que não há cobertura de código do construtor privado da classe. Como podemos adicionar cobertura de teste a um construtor privado?
java
testing
code-coverage
Yegor256
fonte
fonte
Respostas:
Bem, existem maneiras de usar o reflexo, etc. - mas realmente vale a pena? Este é um construtor que nunca deve ser chamado , certo?
Se houver uma anotação ou algo semelhante que você possa adicionar à classe para fazer o Cobertura entender que ela não será chamada, faça isso: não acho que vale a pena se esforçar para adicionar cobertura artificialmente.
EDIT: Se não há como fazer, apenas viva com a cobertura ligeiramente reduzida. Lembre-se de que a cobertura deve ser útil para você - você deve ser o responsável pela ferramenta, e não o contrário.
fonte
Não concordo inteiramente com Jon Skeet. Eu acho que se você pode obter uma vitória fácil para lhe dar cobertura e eliminar o ruído em seu relatório de cobertura, então você deve fazer isso. Diga à sua ferramenta de cobertura para ignorar o construtor ou coloque o idealismo de lado e escreva o seguinte teste e pronto:
fonte
constructor
? Não deveConstructor
ser parametrizado e não um tipo bruto?constructor.isAccessible()
sempre retorna falso, mesmo em um construtor público. Deve-se usarassertTrue(Modifier.isPrivate(constructor.getModifiers()));
.Embora não seja necessariamente para cobertura, criei este método para verificar se a classe de utilitário está bem definida e fazer um pouco de cobertura também.
Coloquei o código completo e exemplos em https://github.com/trajano/maven-jee6/tree/master/maven-jee6-test
fonte
Modifier.isPrivate
comoisAccessible
estava voltandotrue
para construtores privados em alguns casos (simulando interferência de biblioteca?).Assert.utilityClassWellDefined()
no JUnit 4.12+. Você considerou uma solicitação de pull?setAccessible()
para tornar o construtor acessível causa problemas para a ferramenta de cobertura de código do Sonar (quando faço isso, a classe desaparece dos relatórios de cobertura de código do Sonar).Eu tornei privado o construtor da minha classe de funções utilitárias estáticas, para satisfazer CheckStyle. Mas assim como o pôster original, tive Cobertura reclamando do teste. No início, tentei essa abordagem, mas isso não afeta o relatório de cobertura porque o construtor nunca é realmente executado. Então, na verdade, todos esses testes são se o construtor está permanecendo privado - e isso se torna redundante pela verificação de acessibilidade no teste subsequente.
Segui a sugestão de Javid Jamae e usei reflexão, mas acrescentei afirmações para pegar alguém mexendo com a classe sendo testada (e nomeei o teste para indicar altos níveis do mal).
Isso é um exagero, mas tenho que admitir que gosto da sensação calorosa de 100% de cobertura de método.
fonte
fail(...)
não é necessário.Com o Java 8 , é possível encontrar outra solução.
Presumo que você simplesmente deseja criar uma classe de utilitário com poucos métodos estáticos públicos. Se você pode usar o Java 8, então você pode usar no
interface
lugar.Não há construtor e nem reclamação da Cobertura. Agora você precisa testar apenas as linhas de seu interesse.
fonte
O raciocínio por trás do código de teste que não faz nada é atingir 100% de cobertura de código e notar quando a cobertura de código cai. Caso contrário, pode-se sempre pensar, ei, eu não tenho mais 100% de cobertura de código, mas é PROVAVELMENTE por causa dos meus construtores privados. Isso facilita a localização de métodos não testados sem ter que verificar se era apenas um construtor privado. Conforme sua base de código cresce, você vai realmente sentir uma sensação de calor agradável olhando para 100% em vez de 99%.
IMO, é melhor usar reflexão aqui, caso contrário, você teria que obter uma ferramenta de cobertura de código melhor que ignore esses construtores ou de alguma forma dizer à ferramenta de cobertura de código para ignorar o método (talvez uma anotação ou um arquivo de configuração) porque então você ficaria preso com uma ferramenta de cobertura de código específica.
Em um mundo perfeito, todas as ferramentas de cobertura de código ignorariam os construtores privados que pertencem a uma classe final, porque o construtor está lá como uma medida de "segurança", nada mais :)
Em seguida, basta adicionar classes ao array conforme você avança.Eu usaria este código:
fonte
As versões mais recentes do Cobertura têm suporte integrado para ignorar getters / setters / construtores triviais:
https://github.com/cobertura/cobertura/wiki/Ant-Task-Reference#ignore-trivial
Ignorar trivial
Ignorar trivial permite a capacidade de excluir construtores / métodos que contêm uma linha de código. Alguns exemplos incluem uma chamada apenas para um superconstrutor, métodos getter / setter etc. Para incluir o argumento ignorar trivial, adicione o seguinte:
ou em uma versão do Gradle:
fonte
Não. Qual é o objetivo de testar um construtor vazio? Como a cobertura 2.0 existe uma opção para ignorar esses casos triviais (junto com setters / getters), você pode habilitá-lo no maven adicionando a seção de configuração ao plugin cobertura maven:
Alternativamente, você pode usar Cobertura Anotações :
@CoverageIgnore
.fonte
Finalmente, existe uma solução!
fonte
Enum<E>
para realmente ser um enum ... Eu acredito que revela melhor a intenção.Não sei sobre o Cobertura, mas uso o Clover e ele tem um meio de adicionar exclusões de correspondência de padrões. Por exemplo, tenho padrões que excluem as linhas de log do apache-commons para que não sejam contadas na cobertura.
fonte
Outra opção é criar um inicializador estático semelhante ao seguinte código
Dessa forma, o construtor privado é considerado testado e a sobrecarga do tempo de execução basicamente não é mensurável. Eu faço isso para obter 100% de cobertura usando EclEmma, mas provavelmente funciona para todas as ferramentas de cobertura. A desvantagem dessa solução, é claro, é que você escreve o código de produção (o inicializador estático) apenas para fins de teste.
fonte
ClassUnderTest testClass = Whitebox.invokeConstructor (ClassUnderTest.class);
fonte
Às vezes, o Cobertura marca o código que não deve ser executado como 'não coberto', não há nada de errado com isso. Por que você está preocupado em ter
99%
cobertura em vez de100%
?Tecnicamente, porém, você ainda pode invocar esse construtor com reflexão, mas parece muito errado para mim (neste caso).
fonte
Se eu fosse adivinhar a intenção de sua pergunta, diria:
Para 1, é óbvio que você deseja que toda a inicialização seja feita através dos métodos de fábrica. Nesses casos, seus testes devem ser capazes de testar os efeitos colaterais do construtor. Isso deve cair na categoria de teste de método privado normal. Faça os métodos menores para que eles façam apenas um número limitado de coisas determinadas (idealmente, apenas uma coisa e uma coisa bem) e, em seguida, teste os métodos que dependem deles.
Por exemplo, se meu construtor [privado] configurar os campos de instância de minha classe
a
para5
. Então eu posso (ou melhor, devo) testá-lo:Para 2, você pode configurar o trevo para excluir construtores Util se você tiver um padrão de nomenclatura definido para classes Util. Por exemplo, em meu próprio projeto eu uso algo assim (porque seguimos a convenção de que os nomes de todas as classes Util devem terminar com Util):
Eu deixei deliberadamente de fora um
.*
seguinte)
porque tais construtores não pretendem lançar exceções (eles não pretendem fazer nada).É claro que pode haver um terceiro caso em que você pode querer ter um construtor vazio para uma classe não utilitária. Nesses casos, recomendo que você coloque um
methodContext
com a assinatura exata do construtor.Se você tiver muitas dessas classes excepcionais, poderá escolher modificar o construtor privado generalizado reg-ex que sugeri e remover
Util
dele. Nesse caso, você terá que se certificar manualmente de que os efeitos colaterais do construtor ainda sejam testados e cobertos por outros métodos em sua classe / projeto.fonte
Test.java é o seu arquivo de origem, que tem seu construtor privado
fonte
O seguinte funcionou para mim em uma classe criada com a anotação do Lombok @UtilityClass, que adiciona automaticamente um construtor privado.
Embora constructor.setAccessible (true) deva funcionar quando o construtor privado foi escrito manualmente, com a anotação do Lombok não funciona, uma vez que a força. Constructor.newInstance () realmente testa se o construtor é invocado e isso completa a cobertura do próprio construtor. Com o assertThrows você evita que o teste falhe e gerenciou a exceção, pois é exatamente o erro que você esperava. Embora esta seja uma solução alternativa e eu não aprecie o conceito de "cobertura de linha" versus "cobertura de funcionalidade / comportamento", podemos encontrar um sentido neste teste. Na verdade, você tem certeza de que a Classe Utilitário possui um Construtor privado que lança uma exceção corretamente quando invocado também por meio de reflexão. Espero que isto ajude.
fonte
Minha opção preferida em 2019: Use lombok.
Especificamente, a
@UtilityClass
anotação . (Infelizmente, apenas "experimental" no momento em que este livro foi escrito, mas funciona muito bem e tem uma perspectiva positiva, portanto, provavelmente em breve será atualizado para estável.)Essa anotação adicionará o construtor privado para evitar a instanciação e tornará a classe final. Quando combinado com
lombok.addLombokGeneratedAnnotation = true
inlombok.config
, praticamente todas as estruturas de teste irão ignorar o código gerado automaticamente ao calcular a cobertura do teste, permitindo que você ignore a cobertura desse código gerado automaticamente sem hacks ou reflexão.fonte
Você não pode.
Aparentemente, você está criando o construtor privado para evitar a instanciação de uma classe destinada a conter apenas métodos estáticos. Em vez de tentar obter cobertura desse construtor (o que exigiria que a classe fosse instanciada), você deve se livrar dele e confiar que seus desenvolvedores não adicionarão métodos de instância à classe.
fonte