Item 35. 작명 패턴 대신 어노테이션을 사용하라.

1.5 버전 이전에는 작명 패턴(naming pattern) 사용함, 이에 따른 단점들…

어노테이션을 이용한 해결책

표준 어노테이션 정의
import java.lang.annotation.*
/**
 * 어노테이션이 붙은 함수가 테스트 함수임을 표시.
 * 무인자(parameterless) 정적 함수에만 사용가능
 */
@Retention(RegentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
}
표준 어노테이션을 처리하는 코드
public class RunTests{
    public static void main(String[] args){
        int tests = 0;
        int passed = 0;
        Class testClass = Class.forName(args[0]);
        
        for(Method m : testClass.getDeclaredMethods()){
            if( m.isAnnotationPresent(Test.class)){
                tests++;
                try{
                    m.invoke(null);
                    passed++;
                }catch(InvocationTargetException wrappedExc){
                    Throwable exc = wrappedExc.getCause();
                    System.out.println(m + " failed: " + exc);
                }catch(Exception exception){
                    System.out.println("INVALID @Test: " + m);
                }
            }
        }
        System.out.printf("Passed: %d, Failed: %d%n", passed, tests - passed);
    }
}
배열 인자를 추가한 어노테이션
// 배열을 인자로 받는 어노테이션 자료형
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTest {
    Class<? extends Exception>[] value();
}
배열 인자를 받는 어노테이션의 사용
public class Sample{
    @ExceptionTest(ArithmeticException.class)
    public static void test1(){ // 이 테스트는 성공 해야 함
        int i = 0;
        i = i / i;
    }
    
    @ExceptionTest(ArithmeticException.class)
    public static void test2(){ // 이 테스트는 실패 해야 함(엉뚱한 예외 발생)
        int[] a = new int[0];
        int i = a[1];
    }
    
    @ExceptionTest(ArithmeticException.class)
    public static void test3(){ // 이 테스트는 실패 해야 함(예외가 발생하지 않음)
    }
    
}
배열 인자 어노테이션을 처리하는 코드
public class RunTests{
    public static void main(String[] args){
        int tests = 0;
        int passed = 0;
        Class testClass = Class.forName(args[0]);
        
        for(Method m : testClass.getDeclaredMethods()){
            if( m.isAnnotationPresent(Test.class)){
                tests++;
                try{
                    m.invoke(null);
                    passed++;
                }catch(Throwable wrappedExc){
                    Throwable exc = wrappedExc.getCause();
                    Class<? extends Excetpion>[] excTypes = m.getAnnotation(ExceptionTest.class).value();
                    int oldPassed = passed;
                    for (Class<? extends Exception> excType : excTypes){
                        if(excType.isInstance(exc)){
                            passed++;
                            break;
                        }
                    }
                    if( passed == oldPassed)
                        System.out.printf("Test %s failed: %s %n",m, exc);
                }
            }
        }
        System.out.println("Passed: %d, Failed: %d%n", passed, tests - passed);
    }
}

결론