Java Custom Annotations Example

author image

java-custom-annotation

In this tutorial, we will show you how to create two custom annotations – @Test and @TestInfo, to simulate a simple unit test framework.

P.S This unit test example is inspired by this official Java annotation article.

1. @Test Annotation

This @interface tells Java this is a custom annotation. Later, you can annotate it on method level like this @Test(enable=false).

Test.java

                              package com.mkyong.test.core;  import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;  @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) //can use in method only. public @interface Test { 	 	//should ignore this test? 	public boolean enabled() default true; 	 }                          

Note
Method declarations must not have any parameters or a throws clause. Return types are restricted to primitives, String, Class, enums, annotations, and arrays of the preceding types.

2. @TesterInfo Annotation

This @TesterInfo is applied on class level, store the tester details. This shows the different use of return types – enum, array and string.

TesterInfo.java

                              package com.mkyong.test.core;  import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;  @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) //on class level public @interface TesterInfo {  	public enum Priority { 	   LOW, MEDIUM, HIGH 	}  	Priority priority() default Priority.MEDIUM; 	 	String[] tags() default ""; 	 	String createdBy() default "Mkyong"; 	 	String lastModified() default "03/01/2014";  }                          

3. Unit Test Example

Create a simple unit test example, and annotated with the new custom annotations – @Test and @TesterInfo.

TestExample.java

                              package com.mkyong.test;  import com.mkyong.test.core.Test; import com.mkyong.test.core.TesterInfo; import com.mkyong.test.core.TesterInfo.Priority;  @TesterInfo( 	priority = Priority.HIGH,  	createdBy = "mkyong.com",   	tags = {"sales","test" } ) public class TestExample {  	@Test 	void testA() { 	  if (true) 		throw new RuntimeException("This test always failed"); 	}  	@Test(enabled = false) 	void testB() { 	  if (false) 		throw new RuntimeException("This test always passed"); 	}  	@Test(enabled = true) 	void testC() { 	  if (10 > 1) { 		// do nothing, this test always passed. 	  } 	}  }                          

4. Java reflection – Read the Annotation

Below example show you how to use Java reflection APIs to read and process the custom annotations.

RunTest.java

                                  package com.mkyong.test;  import java.lang.annotation.Annotation; import java.lang.reflect.Method;  import com.mkyong.test.core.Test; import com.mkyong.test.core.TesterInfo;  public class RunTest {    public static void main(String[] args) throws Exception {  	System.out.println("Testing...");  	int passed = 0, failed = 0, count = 0, ignore = 0;  	Class<TestExample> obj = TestExample.class;  	// Process @TesterInfo 	if (obj.isAnnotationPresent(TesterInfo.class)) {  		Annotation annotation = obj.getAnnotation(TesterInfo.class); 		TesterInfo testerInfo = (TesterInfo) annotation;  		System.out.printf("%nPriority :%s", testerInfo.priority()); 		System.out.printf("%nCreatedBy :%s", testerInfo.createdBy()); 		System.out.printf("%nTags :");  		int tagLength = testerInfo.tags().length; 		for (String tag : testerInfo.tags()) { 			if (tagLength > 1) { 				System.out.print(tag + ", "); 			} else { 				System.out.print(tag); 			} 			tagLength--; 		}  		System.out.printf("%nLastModified :%s%n%n", testerInfo.lastModified());  	}  	// Process @Test 	for (Method method : obj.getDeclaredMethods()) {  		// if method is annotated with @Test 		if (method.isAnnotationPresent(Test.class)) {  			Annotation annotation = method.getAnnotation(Test.class); 			Test test = (Test) annotation;  			// if enabled = true (default) 			if (test.enabled()) {  			  try { 				method.invoke(obj.newInstance()); 				System.out.printf("%s - Test '%s' - passed %n", ++count, method.getName()); 				passed++; 			  } catch (Throwable ex) { 				System.out.printf("%s - Test '%s' - failed: %s %n", ++count, method.getName(), ex.getCause()); 				failed++; 			  }  			} else { 				System.out.printf("%s - Test '%s' - ignored%n", ++count, method.getName()); 				ignore++; 			}  		}  	} 	System.out.printf("%nResult : Total : %d, Passed: %d, Failed %d, Ignore %d%n", count, passed, failed, ignore);  	} }                              

Output

                                  Testing...  Priority :HIGH CreatedBy :mkyong.com Tags :sales, test LastModified :03/01/2014  1 - Test 'testA' - failed: java.lang.RuntimeException: This test always failed  2 - Test 'testC' - passed  3 - Test 'testB' - ignored  Result : Total : 3, Passed: 1, Failed 1, Ignore 1                              

Done.

References

  1. Wikipedia : Java annotation
  2. Oracle JavaSE docs – annotations
  3. ElementType JavaDoc
  4. RetentionPolicy JavaDoc

author image

Comments