unitarias tutorial test software pruebas español ejercicios configuracion java unit-testing annotations annotation-processing

tutorial - ¿Cómo escribir pruebas unitarias automatizadas para el procesador de anotación java?



testing en java (6)

Estoy experimentando con procesadores de anotación java. Soy capaz de escribir pruebas de integración usando el "JavaCompiler" (de hecho estoy usando "hickory" en este momento). Puedo ejecutar el proceso de compilación y analizar la salida. El problema: una sola prueba se ejecuta durante aproximadamente medio segundo, incluso sin ningún código en mi procesador de anotaciones. Esto es demasiado largo para usarlo en el estilo TDD.

Burlarme de las dependencias me parece muy difícil (tendría que burlarme de todo el paquete "javax.lang.model.element"). ¿Alguien ha tenido éxito en escribir pruebas unitarias para un procesador de anotaciones (Java 6)? Si no ... ¿cuál sería tu enfoque?


Es correcto burlarse de la API de procesamiento de anotaciones (con una biblioteca simulada como easymock) es doloroso. Probé este enfoque y se rompió muy rápidamente. Tienes que configurar muchas expectativas de llamada de método. Las pruebas se vuelven inalcanzables.

Un enfoque de prueba basado en el estado me funcionó razonablemente bien. Tuve que implementar las partes de la API javax.lang.model. * Que necesitaba para mis pruebas . (Que eran solo <350 líneas de código).

Esta es la parte de una prueba para iniciar los objetos javax.lang.model. Después de la configuración, el modelo debe estar en el mismo estado que la implementación del compilador Java.

DeclaredType typeArgument = declaredType(classElement("returnTypeName")); DeclaredType validReturnType = declaredType(interfaceElement(GENERATOR_TYPE_NAME), typeArgument); TypeParameterElement typeParameter = typeParameterElement(); ExecutableElement methodExecutableElement = Model.methodExecutableElement(name, validReturnType, typeParameter);

Los métodos de fábrica estáticos se definen en el Model clase que implementa las clases javax.lang.model. *. Por ejemplo declaredType . (Todas las operaciones no admitidas arrojarán excepciones).

public static DeclaredType declaredType(final Element element, final TypeMirror... argumentTypes) { return new DeclaredType(){ @Override public Element asElement() { return element; } @Override public List<? extends TypeMirror> getTypeArguments() { return Arrays.asList(argumentTypes); } @Override public String toString() { return format("DeclareTypeModel[element=%s, argumentTypes=%s]", element, Arrays.toString(argumentTypes)); } @Override public <R, P> R accept(TypeVisitor<R, P> v, P p) { return v.visitDeclared(this, p); } @Override public boolean equals(Object obj) { throw new UnsupportedOperationException(); } @Override public int hashCode() { throw new UnsupportedOperationException(); } @Override public TypeKind getKind() { throw new UnsupportedOperationException(); } @Override public TypeMirror getEnclosingType() { throw new UnsupportedOperationException(); } }; }

El resto de la prueba verifica el comportamiento de la clase bajo prueba.

Method actual = new Method(environment(), methodExecutableElement); Method expected = new Method(..); assertEquals(expected, actual);

Puede ver el código fuente de las pruebas del generador de código fuente Quickcheck @Samples y @Iterables . (El código aún no es óptimo. La clase de Método tiene muchos parámetros y la clase de Parámetro no se prueba en su propia prueba, sino como parte de la prueba de Método. Sin embargo, debe ilustrar el enfoque.)

Viel Glück!


Esta es una pregunta antigua, pero parece que el estado de las pruebas del procesador de anotaciones no había mejorado, por lo que lanzamos Compile Testing hoy. Los mejores documentos están en package-info.java , pero la idea general es que existe una API fluida para probar la salida de compilación cuando se ejecuta con un procesador de anotaciones. Por ejemplo,

ASSERT.about(javaSource()) .that(JavaFileObjects.forResource("HelloWorld.java")) .processedWith(new MyAnnotationProcessor()) .compilesWithoutError() .and().generatesSources(JavaFileObjects.forResource("GeneratedHelloWorld.java"));

comprueba que el procesador genere un archivo que coincida con GeneratedHelloWorld.java (archivo dorado en la ruta de clase). También puede probar que el procesador produce salida de error:

JavaFileObject fileObject = JavaFileObjects.forResource("HelloWorld.java"); ASSERT.about(javaSource()) .that(fileObject) .processedWith(new NoHelloWorld()) .failsToCompile() .withErrorContaining("No types named HelloWorld!").in(fileObject).onLine(23).atColumn(5);

Esto es obviamente mucho más simple que la burla y, a diferencia de las pruebas de integración típicas, toda la salida se almacena en la memoria.


Estaba en una situación similar, así que creé la biblioteca Avatar . No le dará el rendimiento de una prueba de unidad pura sin compilación, pero si se usa correctamente, no debería ver un gran impacto en el rendimiento.

Avatar le permite escribir un archivo fuente, anotarlo y convertirlo en elementos en una prueba de unidad. Esto le permite realizar una prueba unitaria de los métodos y clases que consumen objetos Element, sin invocar manualmente javac.


He utilizado http://hg.netbeans.org/core-main/raw-file/default/openide.util.lookup/test/unit/src/org/openide/util/test/AnnotationProcessorTestUtils.java aunque esto se basa en java.io.File por simplicidad y por lo tanto tiene la sobrecarga de rendimiento de la que se queja.

La sugerencia de Thomas de burlarse de todo el entorno JSR 269 llevaría a una prueba de unidad pura. En su lugar, es posible que desee escribir más de una prueba de integración que verifique cómo se ejecuta realmente su procesador dentro de javac, lo que le garantiza que es correcto, pero simplemente desea evitar los archivos de disco. Hacer esto requeriría que escribieras un JavaFileManager simulado, que desafortunadamente no es tan fácil como parece y no tengo ejemplos útiles, pero no deberías burlarte de otras cosas como las interfaces Element .


Una opción es agrupar todas las pruebas en una clase. Medio segundo para compilar, etc. es más que una constante para un conjunto dado de pruebas, supongo que el tiempo real de prueba para una prueba es inteligible.


jOOR es una pequeña biblioteca de reflexión de Java que también proporciona acceso simplificado a la API de compilación de Java en memoria en javax.tool.JavaCompiler . Agregamos soporte para esto a los procesadores de anotación de jOOQ de prueba unitaria . Fácilmente puedes escribir pruebas unitarias como esta:

@Test public void testCompileWithAnnotationProcessors() { AProcessor p = new AProcessor(); try { Reflect.compile( "org.joor.test.FailAnnotationProcessing", "package org.joor.test; " + "@A " + "public class FailAnnotationProcessing { " + "}", new CompileOptions().processors(p) ).create().get(); Assert.fail(); } catch (ReflectException expected) { assertFalse(p.processed); } }

El ejemplo anterior ha sido tomado de esta publicación de blog.