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

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);
    }
}

결론