Este es el código:
package com.XXX;
public final class Foo {
private Foo() {
// intentionally empty
}
public static int bar() {
return 1;
}
}
Esta es la prueba:
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 bien, la clase está probada. Pero Cobertura dice que no hay cobertura de código del constructor privado de la clase. ¿Cómo podemos agregar cobertura de prueba a un constructor tan privado?
java
testing
code-coverage
yegor256
fuente
fuente
Respuestas:
Bueno, hay formas en las que podrías usar la reflexión, etc., pero ¿realmente vale la pena? Este es un constructor que nunca debería llamarse , ¿verdad?
Si hay una anotación o algo similar que pueda agregar a la clase para que Cobertura entienda que no se llamará, hágalo: no creo que valga la pena pasar por aros para agregar cobertura artificialmente.
EDITAR: Si no hay forma de hacerlo, simplemente viva con la cobertura ligeramente reducida. Recuerde que la cobertura está destinado a ser algo que es útil para usted - usted debe estar a cargo de la herramienta, y no al revés.
fuente
No estoy del todo de acuerdo con Jon Skeet. Creo que si puede obtener una ganancia fácil para brindarle cobertura y eliminar el ruido en su informe de cobertura, entonces debería hacerlo. Dile a tu herramienta de cobertura que ignore al constructor, o deja a un lado el idealismo y escribe la siguiente prueba y termina con ella:
fuente
constructor
? ¿NoConstructor
debería parametrizarse y no ser un tipo sin formato?constructor.isAccessible()
siempre devuelve falso, incluso en un constructor público. Uno debería usarassertTrue(Modifier.isPrivate(constructor.getModifiers()));
.Aunque no es necesariamente para cobertura, creé este método para verificar que la clase de utilidad esté bien definida y que también tenga un poco de cobertura.
He colocado el código completo y los ejemplos en https://github.com/trajano/maven-jee6/tree/master/maven-jee6-test
fuente
Modifier.isPrivate
comoisAccessible
regresabatrue
para constructores privados en algunos casos (¿burlarse de la interferencia de la biblioteca?).Assert.utilityClassWellDefined()
en JUnit 4.12+. ¿Ha considerado una solicitud de extracción?setAccessible()
para hacer accesible al constructor causa problemas para la herramienta de cobertura de código de Sonar (cuando hago esto, la clase desaparece de los informes de cobertura de código de Sonar).Hice privado el constructor de mi clase de funciones de utilidad estática, para satisfacer CheckStyle. Pero al igual que el póster original, Cobertura se quejó de la prueba. Al principio probé este enfoque, pero esto no afecta el informe de cobertura porque el constructor nunca se ejecuta realmente. Entonces, realmente todas estas pruebas son si el constructor permanece privado, y esto se vuelve redundante por la verificación de accesibilidad en la prueba posterior.
Seguí la sugerencia de Javid Jamae y usé la reflexión, pero agregué afirmaciones para atrapar a cualquiera que se metiera con la clase que se estaba probando (y nombré la prueba para indicar High Levels Of Evil).
Esto es tan exagerado, pero debo admitir que me gusta la sensación cálida y difusa de la cobertura del método al 100%.
fuente
fail(...)
es necesario simplemente alinear con .Con Java 8 , es posible encontrar otra solución.
Supongo que simplemente desea crear una clase de utilidad con pocos métodos estáticos públicos. Si puede usar Java 8, puede usarlo
interface
en su lugar.No hay constructor y no se quejan de Cobertura. Ahora necesita probar solo las líneas que realmente le interesan.
fuente
El razonamiento detrás de probar el código que no hace nada es lograr una cobertura del código del 100% y darse cuenta de cuándo cae la cobertura del código. De lo contrario, uno siempre podría pensar, oye, ya no tengo una cobertura de código del 100%, pero PROBABLEMENTE se debe a mis constructores privados. Esto facilita la detección de métodos no probados sin tener que verificar que solo sea un constructor privado. A medida que su código base crece, realmente sentirá una agradable sensación de calor al mirar al 100% en lugar del 99%.
En mi opinión, es mejor usar la reflexión aquí, ya que de lo contrario tendría que obtener una mejor herramienta de cobertura de código que ignore estos constructores o de alguna manera decirle a la herramienta de cobertura de código que ignore el método (tal vez una anotación o un archivo de configuración) porque entonces estaría atascado con una herramienta de cobertura de código específica.
En un mundo perfecto, todas las herramientas de cobertura de código ignorarían los constructores privados que pertenecen a una clase final porque el constructor está allí como una medida de "seguridad", nada más :)
Y luego simplemente agregue clases a la matriz a medida que avanza.Yo usaría este código:
fuente
Las versiones más nuevas de Cobertura tienen soporte incorporado para ignorar captadores / definidores / constructores triviales:
https://github.com/cobertura/cobertura/wiki/Ant-Task-Reference#ignore-trivial
Ignorar trivial
Ignorar trivial permite la posibilidad de excluir constructores / métodos que contienen una línea de código. Algunos ejemplos incluyen una llamada a un superconstrctor solamente, métodos getter / setter, etc. Para incluir el argumento trivial ignorar agregue lo siguiente:
o en una compilación de Gradle:
fuente
No lo hagas. ¿Qué sentido tiene probar un constructor vacío? Dado que cobertura 2.0 hay una opción para ignorar estos casos triviales (junto con setters / getters), puede habilitarlo en maven agregando la sección de configuración al complemento cobertura maven:
Alternativamente, puede utilizar las anotaciones de cobertura :
@CoverageIgnore
.fuente
¡Finalmente, hay una solución!
fuente
Enum<E>
para ser realmente una enumeración ... Creo que eso revela mejor la intención.No sé sobre Cobertura, pero uso Clover y tiene un medio para agregar exclusiones de coincidencia de patrones. Por ejemplo, tengo patrones que excluyen líneas apache-commons-logging para que no se cuenten en la cobertura.
fuente
Otra opción es crear un inicializador estático similar al siguiente código
De esta manera, el constructor privado se considera probado y la sobrecarga del tiempo de ejecución básicamente no es medible. Hago esto para obtener una cobertura del 100% con EclEmma, pero es probable que funcione para todas las herramientas de cobertura. El inconveniente de esta solución, por supuesto, es que escribe el código de producción (el inicializador estático) solo con fines de prueba.
fuente
ClassUnderTest testClass = Whitebox.invokeConstructor (ClassUnderTest.class);
fuente
A veces, Cobertura marca el código que no está destinado a ejecutarse como 'no cubierto', no hay nada de malo en eso. ¿Por qué le preocupa tener
99%
cobertura en lugar de100%
?Técnicamente, sin embargo, todavía puedes invocar ese constructor con reflexión, pero me suena muy mal (en este caso).
fuente
Si tuviera que adivinar la intención de su pregunta, diría:
Para 1, es obvio que desea que toda la inicialización se realice a través de métodos de fábrica. En tales casos, sus pruebas deberían poder probar los efectos secundarios del constructor. Esto debería caer en la categoría de prueba de método privado normal. Haga los métodos más pequeños para que solo hagan un número limitado de cosas determinadas (idealmente, solo una cosa y una cosa bien) y luego pruebe los métodos que se basan en ellos.
Por ejemplo, si mi constructor [privado] configura los campos de instancia de mi clase
a
en5
. Entonces puedo (o más bien debo) probarlo:Para 2, puede configurar clover para excluir los constructores Util si tiene un patrón de nomenclatura establecido para las clases Util. Por ejemplo, en mi propio proyecto utilizo algo como esto (porque seguimos la convención de que los nombres de todas las clases de Util deben terminar con Util):
Deliberadamente he omitido un
.*
siguiente)
porque tales constructores no están destinados a lanzar excepciones (no están destinados a hacer nada).Por supuesto, puede haber un tercer caso en el que desee tener un constructor vacío para una clase que no sea de utilidad. En tales casos, le recomendaría que coloque un
methodContext
con la firma exacta del constructor.Si tiene muchas de estas clases excepcionales, puede elegir modificar el constructor privado generalizado reg-ex que sugerí y eliminarlo
Util
. En este caso, deberá asegurarse manualmente de que los efectos secundarios de su constructor aún se prueben y cubran con otros métodos en su clase / proyecto.fuente
Test.java es su archivo fuente, que tiene su constructor privado
fuente
Lo siguiente me funcionó en una clase creada con la anotación de Lombok @UtilityClass, que agrega automáticamente un constructor privado.
Aunque constructor.setAccessible (true) debería funcionar cuando el constructor privado se ha escrito manualmente, con la anotación de Lombok no funciona, ya que la fuerza. Constructor.newInstance () en realidad prueba que se invoca al constructor y esto completa la cobertura del propio constructor. Con assertThrows evitas que la prueba falle y manejaste la excepción ya que es exactamente el error que esperas. Aunque esta es una solución alternativa y no aprecio el concepto de "cobertura de línea" frente a "cobertura de funcionalidad / comportamiento", podemos encontrar un sentido en esta prueba. De hecho, está seguro de que la clase de utilidad tiene en realidad un constructor privado que arroja correctamente una excepción cuando se invoca también mediante reflacción. Espero que esto ayude.
fuente
Mi opción preferida en 2019: usar lombok.
Específicamente, la
@UtilityClass
anotación . (Lamentablemente, solo es "experimental" en el momento de escribir este artículo, pero funciona bien y tiene una perspectiva positiva, por lo que es probable que pronto se actualice a estable).Esta anotación agregará el constructor privado para evitar la creación de instancias y hará que la clase sea final. Cuando se combinan con
lombok.addLombokGeneratedAnnotation = true
inlombok.config
, casi todos los marcos de prueba ignorarán el código generado automáticamente al calcular la cobertura de la prueba, lo que le permitirá omitir la cobertura de ese código generado automáticamente sin hacks ni reflejos.fuente
No puedes.
Aparentemente, está creando el constructor privado para evitar la creación de instancias de una clase que está destinada a contener solo métodos estáticos. En lugar de intentar obtener cobertura de este constructor (lo que requeriría que la clase sea instanciada), debe deshacerse de él y confiar en que sus desarrolladores no agregarán métodos de instancia a la clase.
fuente