λͺ
λͺ
ν¨ν΄λ³΄λ€ μ λν
μ΄μ
μ μ¬μ©νλΌ.
μμ μ λͺ
λͺ
ν¨ν΄μ μ¬μ©ν΄ νλ‘κ·Έλ¨ μμμ μ 보λ₯Ό νμνλ€. νμ§λ§ λͺ
λͺ
ν¨ν΄μ νλ‘κ·Έλ¨ μμλ₯Ό λ§€μ° λΆνΈνκ² λ§λ λ€.
μλ₯Όλ€μ΄ μ΄μ μ Junitμ ν
μ€νΈ λ©μλλ₯Ό test
λ‘ μμνλ μ΄λ¦μΌλ‘ μ§μ΄μΌ ν
μ€νΈλ₯Ό μ€ννκΈ° λλ¬Έμ, μ€νλ μλνμ§ μμ λ©μλκ° ν
μ€νΈ λμμ΄ λλ κ²½μ°κ° λ§μλ€.
μ λν
μ΄μ
λ°©λ²μΌλ‘ λͺ
λͺ
ν¨ν΄μ μμ ν λ체ν μ μκΈ° λλ¬Έμ λͺ
λͺ
ν¨ν΄μ μ¬μ©νμ§ λ§κ³ μ λν
μ΄μ
μ μ¬μ©νλ κ²μ΄ μ’λ€.
Annotation
μ λν
μ΄μ
μ ν΄λμ€, λ©μλ, νλ λ±μ νλ‘κ·Έλ¨ μμμ λΆκ° μ 보λ₯Ό λ§λΆμ΄λ λ°©λ²μ΄λ€.
μ΄ λ°©λ²μ μ¬μ©νλ©΄ μμ λͺ
λͺ
ν¨ν΄μ λ¨μ μ λͺ¨λ ν΄κ²°νλ©΄μ κ°κ²°νκ³ λͺ
ννκ² νλ‘κ·Έλ¨ μμμ μλ―Έλ₯Ό μ λ¬ν μ μλ€.
λ§μ»€ μ λν
μ΄μ
Copy
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
}
@Test
μ λν
μ΄μ
μ μ΄ν΄λ³΄λ©΄ @interface
λΌλ ν€μλλ‘ μ μΈλμ΄ μκ³ , @Retention
κ³Ό @Target
μ΄λΌλ μ λν
μ΄μ
μ κ°μ§κ³ μλ€.
μ΄μ κ°μ΄ μ λν
μ΄μ
μ μΈμ λ€λ μ λν
μ΄μ
μ λ©ν μ λν
μ΄μ
(meta-annotation)μ΄λΌκ³ νλ€.
@Retention
: ν΄λΉ μ λν
μ΄μ
μ μΈμ κΉμ§ μ μ§ν κ²μΈμ§λ₯Ό μ§μ , μ¬κΈ°μλ RetentionPolicy.RUNTIME
μΌλ‘ μ§μ λμ΄ μμ΄ @Test
κ° λ°νμμλ μ μ§λμ΄μΌ νλ€λ κ²μ μλ―Έν¨
@Target
: ν΄λΉ μ λν
μ΄μ
μ μ΄λμ μ¬μ©ν μ μλμ§λ₯Ό μ§μ , μ¬κΈ°μλ ElementType.METHOD
λ‘ μ§μ λμ΄ μμ΄ @Test
κ° λ©μλ μ μΈμλ§ μ¬μ©ν μ μμ
μ΄λ κ² μμ±λ @Test
μ λν
μ΄μ
μ λ€μκ³Ό κ°μ΄ μ¬μ©νλλ°, μ΄μ²λΌ 맀κ°λ³μ μμ΄ μ¬μ©ν μ μλ μ λν
μ΄μ
μ λ§μ»€(marker) μ λν
μ΄μ
μ΄λΌκ³ νλ€.
Copy class Sample {
@Test
public static void m1() {
// Test m1
}
}
μ λν
μ΄μ
μ λ§ κ·Έλλ‘ λΆκ° μ 보λ₯Ό λ§λΆμ΄λ κ²μ΄κΈ° λλ¬Έμ, ν΄λΉ μμμ μ§μ μ μΈ μν₯μ μ£Όμ§ μκ³ , μ΄ μ λν
μ΄μ
μ μ²λ¦¬ν νλ‘κ·Έλ¨μκ² μΆκ° μ 보λ₯Ό μ 곡νλ κ²μ΄ μ λΆμ΄λ€.
μ΄λ¬ν λ§μ»€ μ λν
μ΄μ
μ μ²λ¦¬νλ μ½λ μμλ λ€μκ³Ό κ°λ€.
Copy public class RunTests {
public static void main(String[] args) throws Exception {
int tests = 0;
int passed = 0;
Class<?> testClass = Class.forName(args[0]);
for (Method m : testClass.getDeclaredMethods()) { // ν΄λμ€μ λͺ¨λ λ©μλλ₯Ό μν
if (m.isAnnotationPresent(Test.class)) { // @Test μ λν
μ΄μ
μ΄ μ‘΄μ¬νλμ§ νμΈ
tests++;
try {
m.invoke(null);
passed++;
} catch (InvocationTargetException wrappedExc) {
Throwable exc = wrappedExc.getCause();
System.out.println(m + " failed: " + exc);
} catch (Exception exc) {
System.out.println("Invalid @Test: " + m);
}
}
}
System.out.printf("Passed: %d, Failed: %d%n", passed, tests - passed);
}
}
맀κ°λ³μλ₯Ό λ°λ μ λν
μ΄μ
μ λν
μ΄μ
μλ 맀κ°λ³μλ₯Ό λ°μ μ μν μ μλλ°, μ΄λ₯Ό μ¬μ©ν μμλ λ€μκ³Ό κ°λ€.
Copy
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTest {
Class<? extends Throwable> value();
}
@ExceptionTest
μ λν
μ΄μ
μ @Test
μ λν
μ΄μ
κ³Ό λΉμ·νμ§λ§, value
λΌλ 맀κ°λ³μλ₯Ό λ°μμ ν
μ€νΈ λ©μλκ° νΉμ ν μμΈλ₯Ό λμ ΈμΌλ§ μ±κ³΅νλ ν
μ€νΈμμ λͺ
μνλ€.
Copy class Sample {
@ExceptionTest({IndexOutOfBoundsException.class, NullPointerException.class})
public static void doublyBad() {
List<String> list = new ArrayList<>();
list.addAll(5, null);
}
}
ν΄λΉ μ λν
μ΄μ
μ μ²λ¦¬νλ μ½λλ λ€μκ³Ό κ°λ€.
Copy public class RunTests {
public static void main(String[] args) throws Exception {
int tests = 0;
int passed = 0;
Class<?> testClass = Class.forName(args[0]);
for (Method m : testClass.getDeclaredMethods()) { // ν΄λμ€μ λͺ¨λ λ©μλλ₯Ό μν
if (m.isAnnotationPresent(ExceptionTest.class)) { // @ExceptionTest μ λν
μ΄μ
μ΄ μ‘΄μ¬νλμ§ νμΈ
tests++;
try {
m.invoke(null);
System.out.printf("Test %s failed: no exception%n", m);
} catch (Throwable wrappedExc) {
Throwable exc = wrappedExc.getCause();
int oldPassed = passed;
Class<? extends Throwable>[] excTypes = m.getAnnotation(ExceptionTest.class).value(); // @ExceptionTestμ valueλ₯Ό κ°μ Έμ΄
for (Class<? extends Throwable> excType : excTypes) { // @ExceptionTestμ valueλ₯Ό μν
if (excType.isInstance(exc)) {
passed++;
break;
}
}
if (passed == oldPassed) {
System.out.printf("Test %s failed: %s %n", m, exc);
}
}
}
}
System.out.printf("Passed: %d, Failed: %d%n", passed, tests - passed);
}
}
@Repeatable
Java 8λΆν°λ μ λν
μ΄μ
μ λ°λ³΅ν΄μ μ μ©νμ¬ μ¬λ¬ κ°μ κ°μ λ°λ λ°©λ²μ μ 곡νλ€.
κΈ°μ‘΄ μ λν
μ΄μ
μ μμ @Repeatable
μ μΆκ°νκ³ , μ λν
μ΄μ
μ λ΄μ 컨ν
μ΄λ μ λν
μ΄μ
μ μ μνλ©΄ λλ€.
Copy // @Repeatableμ μΆκ°νμ¬ μ λν
μ΄μ
μ λ°λ³΅ν΄μ μ μ©ν μ μλλ‘ ν¨
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Repeatable(ExceptionTestContainer.class)
public @interface ExceptionTest {
Class<? extends Throwable> value();
}
// μ λν
μ΄μ
μ λ΄μ 컨ν
μ΄λ μ λν
μ΄μ
μ μ
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTestContainer {
ExceptionTest[] value();
}
μ΄λ κ² μ μλ μ λν
μ΄μ
μ μ¬μ©νκΈ° μν΄μ λ¨μν μ¬λ¬ κ°μ @ExceptionTest
μ λν
μ΄μ
μ μ μ©νλ©΄ λλ€.
Copy class Sample {
@ExceptionTest(IndexOutOfBoundsException.class)
@ExceptionTest(NullPointerException.class)
public static void doublyBad() {
List<String> list = new ArrayList<>();
list.addAll(5, null);
}
}
μΆκ°μ μΌλ‘ μ λν
μ΄μ
μ μ²λ¦¬νλ μ½λλ νλλ§ λ¬μμ λμ μ¬λ¬ κ°λ₯Ό λ¬μμ λλ₯Ό ꡬλΆνμ¬ μ²λ¦¬ν΄μΌ νλ€.
Copy public class RunTests {
public static void main(String[] args) throws Exception {
int tests = 0;
int passed = 0;
Class<?> testClass = Class.forName(args[0]);
for (Method m : testClass.getDeclaredMethods()) { // ν΄λμ€μ λͺ¨λ λ©μλλ₯Ό μν
if (m.isAnnotationPresent(ExceptionTest.class)
|| m.isAnnotationPresent(ExceptionTestContainer.class)) { // @ExceptionTest λλ @ExceptionTestContainer μ λν
μ΄μ
μ΄ μ‘΄μ¬νλμ§ νμΈ
tests++;
try {
m.invoke(null);
System.out.printf("Test %s failed: no exception%n", m);
} catch (Throwable wrappedExc) {
Throwable exc = wrappedExc.getCause();
int oldPassed = passed;
ExceptionTest[] excTests = m.getAnnotationsByType(ExceptionTest.class); // @ExceptionTest μ λν
μ΄μ
μ κ°μ Έμ΄
for (ExceptionTest excTest : excTests) { // @ExceptionTest μ λν
μ΄μ
μ μν
if (excTest.value().isInstance(exc)) {
passed++;
break;
}
}
if (passed == oldPassed) {
System.out.printf("Test %s failed: %s %n", m, exc);
}
}
}
}
System.out.printf("Passed: %d, Failed: %d%n", passed, tests - passed);
}
}