티스토리 뷰
728x90
반응형
명명패턴보다 애너테이션을 사용하라
- JUnit은 버전 3까지 테스트 메서드 이름을 test로 시작하게끔 하였습니다.
🧨 단점
- 오타에 취약합니다.
- JUnit 3버전에서는 테스트 메서드명을 tsetSafeOverride로 지어버리면 무시하고 테스트를 진행하지 않습니다.
- 올바른 프로그램 요소에서만 사용되리라는 보장이 없습니다.
- 메서드가 아닌 클래스명을 TestSafeOverride로 지어 내부의 메서드가 테스트되길 기대할 수 있지만 JUnit은 클래스명에는 관심이 없으므로 테스트가 실행되지 않습니다.
- 프로그램 요소를 매개변수로 전달할 마땅한 방법이 없습니다.
- 특정 예외를 던져야 발생하는 테스트가 있다는 가정하에 기대하는 예외 타입을 테스트에 매개변수로 전달해야하는데 방법이 마땅치 않습니다.
💡 해결책 - 애너테이션(마커 애너테이션)
- JUnit4에서 도입된 애너테이션은 명명패턴의 단점을 해결해줍니다.
- 해당 KDGTest 애너테이션에 다른 애너테이션이 선언되어 있는데 이를 메타 애너테이션이라고 합니다.
- KDGTest 애너테이션은 아무 매개변수 없이 단순히 대상에 마킹한다는 뜻에서 마커 애너테이션이라고 합니다.
- 추가적인 애너테이션의 종류가 궁금하시다면 애너테이션 살펴보기 >> 이동
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface KDGTest {
}
💡 사용 예시
- 성공 1, 실패 2, 잘못 사용 1, 무시 4
- 해당 @KDGTest 애너테이션은 Sample 클래스의 의미에 직접적으로 영향을 주지 않습니다. 그저 애너테이션에 관심이 있는 프로그램에게 추가 정보를 제공할 뿐입니다.
- 즉 애너테이션이 사용된 코드를 두면 해당 애너테이션에 관심이 있는 IDE에서 특별한 처리를 할 기회를 준다는 것입니다.
public class Sample {
@KDGTest
public static void m1() {} // 성공
public static void m2() {} // 무시
@KDGTest
public static void m3() {
throw new RuntimeException("fail"); // 실패
}
public static void m4() {} // 무시
@KDGTest
public void m5() {} // 무시 (잘못 사용된 예: static 메서드가 아님)
public static void m6() {} // 무시
@KDGTest
public static void m7() {
throw new RuntimeException("fail"); // 실패
}
public static void m8() {} // 무시
}
public class RunTests {
public static void main(String[] args) throws ClassNotFoundException{
int tests = 0;
int passed = 0;
Class<?> testClass = Class.forName("com.effectivejava.study.chapter06.Item39.Sample");
for (Method m : testClass.getDeclaredMethods()) {
if (m.isAnnotationPresent(KDGTest.class)) {
tests++;
try {
m.invoke(null);
passed++;
} catch (InvocationTargetException e) {
Throwable exc = e.getCause();
System.out.println(m + " 실패: " + exc);
} catch (Exception e) {
System.out.println("잘못 사용한 @KDGTest: " + m);
}
}
}
System.out.printf("성공: %d, 실패: %d%n", passed, tests-passed);
}
}
💡 예외를 던지는 애너테이션
- 특정 예외를 던져야만 성공하는 테스트를 만들기 위해 @ExceptionTest 애너테이션을 만들었습니다.
- 타입이 Class<? extends Throwable>인 매개변수를 가집니다.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTest {
Class<? extends Throwable> value();
}
💡 사용 예시
public class Sample2 {
@ExceptionTest(ArithmeticException.class)
public static void m1() {
int i = 0;
i = i / i; // divide by zero. ArithmeticException 예외를 발생시킴 -> 성공
}
@ExceptionTest(ArithmeticException.class)
public static void m2() {
int[] ints = new int[0];
int i = ints[0]; // IndexOutOfBoundsException 발생 -> ArithmeticException가 아니므로 실패
}
@ExceptionTest(ArithmeticException.class)
public static void m3() {} // 아무 Exception도 발생하지 않음 -> 실패
}
public class RunTestsV2 {
public static void main(String[] args) throws ClassNotFoundException{
int tests = 0;
int passed = 0;
Class<?> testClass = Class.forName("com.effectivejava.study.chapter06.Item39.Sample2");
for (Method m : testClass.getDeclaredMethods()) {
tests++;
try {
m.invoke(null);
System.out.printf("테스트 %s 실패: 예외를 던지지 않음%n", m);
} catch (InvocationTargetException e) {
Throwable exc = e.getCause();
Class<? extends Throwable> excType = m.getAnnotation(ExceptionTest.class).value();
if (excType.isInstance(exc)) {
passed++;
} else {
System.out.printf("테스트 %s 실패: 기대한 예외 %s, 발생한 예외 %s%n", m, excType.getName(), exc);
}
} catch (Exception e) {
System.out.println("잘못 사용한 @ExceptionTest: " + m);
}
}
System.out.printf("성공: %d, 실패: %d%n", passed, tests-passed);
}
}
💡 여러 예외를 던지는 애너테이션
- 위에서는 매개변수가 하나인 애너테이션 예제를 보았습니다. 그런데 매개변수가 하나가 아닌 둘 이상인 경우는 어떻게 해야할까요?
// 배열 사용
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTestV2 {
Class<? extends Throwable>[] value();
}
💡 사용 예시
public class Sample3 {
@ExceptionTestV2({
IndexOutOfBoundsException.class,
NullPointerException.class
})
public static void m1() {
List<String> list = new ArrayList<>();
list.addAll(6, null);
}
}
public class RunTestsV3 {
public static void main(String[] args) throws ClassNotFoundException{
int tests = 0;
int passed = 0;
Class<?> testClass = Class.forName("com.effectivejava.study.chapter06.Item39.Sample3");
for (Method m : testClass.getDeclaredMethods()) {
if (m.isAnnotationPresent(ExceptionTestV2.class)) {
tests++;
try {
m.invoke(null);
System.out.printf("테스트 %s 실패: 예외를 던지지 않음%n", m);
} catch (InvocationTargetException e) {
Throwable exc = e.getCause();
int oldPassed = passed;
Class<? extends Throwable>[] excTypes = m.getAnnotation(ExceptionTestV2.class).value();
for (Class<? extends Throwable> excType : excTypes) {
if (excType.isInstance(exc)) {
passed++;
break;
}
}
if (passed == oldPassed) {
System.out.printf("테스트 %s 실패: %s %n", m, exc);
}
} catch (Exception e) {
System.out.println("잘못 사용한 @ExceptionTestV2: " + m);
}
}
}
System.out.printf("성공: %d, 실패: %d%n", passed, tests-passed);
}
}
✔️ 정리
- 애너테이션이 나온 이상 많은 상황에서 명명 패턴보다 애너테이션 활용이 좋습니다.
- 명명 패턴의 단점들을 모두 해결해줍니다.
728x90
반응형
'스터디 > 이펙티브 자바' 카테고리의 다른 글
이펙티브 자바 - Item41. 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라 (0) | 2022.07.29 |
---|---|
이펙티브 자바 - Item40. @Override 애너테이션을 일관되게 사용하라 (0) | 2022.07.29 |
이펙티브 자바 - Item38. 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라. (0) | 2022.07.27 |
이펙티브 자바 - Item36. 비트 필드 대신 EnumSet을 사용하라. (0) | 2022.07.25 |
이펙티브 자바 - Item35. ordinal 메서드 대신 인스턴스 필드를 사용하라. (0) | 2022.07.25 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- pipeline architecture
- 레이어드 아키텍처란
- 트랜잭셔널 아웃박스 패턴 스프링 부트 예제
- service based architecture
- 서비스 기반 아키텍처
- spring boot redisson sorted set
- transactional outbox pattern
- 공간 기반 아키텍처
- pipe and filter architecture
- space based architecture
- spring boot excel download oom
- spring boot redis 대기열 구현
- polling publisher spring boot
- java ThreadLocal
- 자바 백엔드 개발자 추천 도서
- redis 대기열 구현
- redis sorted set으로 대기열 구현
- 트랜잭셔널 아웃박스 패턴 스프링부트
- spring boot redisson destributed lock
- spring boot poi excel download
- @ControllerAdvice
- JDK Dynamic Proxy와 CGLIB의 차이
- 람다 표현식
- java userThread와 DaemonThread
- spring boot 엑셀 다운로드
- spring boot excel download paging
- transactional outbox pattern spring boot
- spring boot redisson 분산락 구현
- redis sorted set
- microkernel architecture
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
글 보관함