JUnit5 병렬 실행
테스트 병렬 실행
기본적으로 모든 테스트 메소드는 단일 스레드에서 순차적으로 실행된다. 여기에서는 병렬로 실행하는 방법에 대해서 소개해 보도록 하겠다.
테스트 병렬 실행하도록 설정
테스트를 병렬로 실행하기 위해서는 먼저 설정 파일이 필요하다.
junit-platform.properties
라는 설정 파일을 클래스 패스 루트에 생성한다.
그러고, 아래와 같이 junit.jupiter.execution.parallel.enabled=true
라고 설정하면 병렬 실행이 활성화 된다.
/test/resource/junit-platform.properties
junit.jupiter.execution.parallel.enabled=true
이 설정만으로는 테스트 메소드는 변함없이 단일 thread로 순차적으로 실행된다.
테스트 메소드를 병렬로 실행하려면 테스트 클래스에 @Execution
어노테이션을 설정하고, value
를 CONCURRENT으로 지정해야 한다.
SAME_THREAD를 지정하면 부모와 동일한 스레드에서 실행된다. 기본값은 이것으로 설정되어 있다.
package com.devkuma.junit5.parallel;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
@Execution(ExecutionMode.CONCURRENT)
public class ParallelTest1 {
@BeforeAll
static void beforeAll() {
printThread("beforeAll()");
}
@BeforeEach
void beforeEach(TestInfo testInfo) {
String name = testInfo.getDisplayName();
printThread(" " + name + ":beforeEach()");
}
@Test
void test1() {
printThread(" test1()");
}
@Test
void test2() {
printThread(" test2()");
}
@AfterEach
void afterEach(TestInfo testInfo) {
String name = testInfo.getDisplayName();
printThread(" " + name + ":afterEach()");
}
@AfterAll
static void afterAll() {
printThread("afterAll()");
}
private static void printThread(String test) {
String name = Thread.currentThread().getName();
System.out.printf("%s@%s%n", test, name);
}
}
실행 결과:
beforeAll()@ForkJoinPool-1-worker-19
test2():beforeEach()@ForkJoinPool-1-worker-23
test1():beforeEach()@ForkJoinPool-1-worker-5
test2()@ForkJoinPool-1-worker-23
test1()@ForkJoinPool-1-worker-5
test1():afterEach()@ForkJoinPool-1-worker-5
test2():afterEach()@ForkJoinPool-1-worker-23
afterAll()@ForkJoinPool-1-worker-19
기본 실행 모드 변경
설정 파일에 junit.jupiter.execution.parallel.mode.default=concurrent
으로 지정하면 기본 실행 모드가 병렬 실행(ExecutionMode.CONCURRENT
을 지정한 상태)이 된다.
/test/resource/junit-platform.properties
junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.default=concurrent
클래스 파일에서는 @Execution
어노테이션을 지정하지 않았다.
package com.devkuma.junit5.parallel;
import org.junit.jupiter.api.Test;
public class ParallelTest2 {
@Test
void test1() {
printThread("test1()");
}
@Test
void test2() {
printThread("test2()");
}
private static void printThread(String test) {
String name = Thread.currentThread().getName();
System.out.printf("%s@%s%n", test, name);
}
}
실행 결과:
test2()@ForkJoinPool-1-worker-23
test1()@ForkJoinPool-1-worker-5
기본 실행 모드가 적용되지 않는 예외
junit.jupiter.execution.parallel.mode.default=concurrent
를 지정하면 기본으로 대부분의 테스트 메소드가 병렬로 실행되게 된다.
단, 예외로서 아래와 같이 설정된 테스트 클래스나 메소드는 디폴트의 실행 모드를 변경해도 병렬 실행되지 않게 된다.
Lifecycle.PER_CLASS
로 지정한 테스트 클래스MethodOrderer.Random
외에 다른MethodOrderer
가 지정된 테스트 메소드
package com.devkuma.junit5.parallel;
import org.junit.jupiter.api.*;
public class ParallelTest3 {
@Nested
class StandardNestedTest {
@Test
void test1() {
printThread("StandardNestedTest.test1()");
}
@Test
void test2() {
printThread("StandardNestedTest.test2()");
}
}
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class PerClassTest {
@Test
void test1() {
printThread("PerClassTest.test1()");
}
@Test
void test2() {
printThread("PerClassTest.test2()");
}
}
@Nested
@TestMethodOrder(MethodOrderer.MethodName.class)
class OrderedTest {
@Test
void test1() {
printThread("OrderedTest.test1()");
}
@Test
void test2() {
printThread("OrderedTest.test2()");
}
}
private static void printThread(String test) {
String name = Thread.currentThread().getName();
System.out.printf("%s@%s%n", test, name);
}
}
실행 결과:
OrderedTest.test1()@ForkJoinPool-1-worker-5
PerClassTest.test1()@ForkJoinPool-1-worker-23
StandardNestedTest.test1()@ForkJoinPool-1-worker-27
StandardNestedTest.test2()@ForkJoinPool-1-worker-19
PerClassTest.test2()@ForkJoinPool-1-worker-23
OrderedTest.test2()@ForkJoinPool-1-worker-5
위에 예제에서 StandardNestedTest
의 테스트 메소드는 다른 thread에서 실행되고 있다(thread명이 다르다).
그러나, PerClassTest
와의 OrderedTest
테스트 메소드는 각각 다른 thread로 실행되고 있다(thread명이 같다).
명시적으로 병렬 실행 지정
@TestMethodOrder
으로 일부러 순서를 지정하고 있는 테스트를 병렬 실행시킨다는 것은 이상한 애기긴 하지만,
이러한 클래스의 테스트 메소드도 병렬 실행시키고 싶은 경우는 그 테스트 클래스가 thread 세이프인 것을 확인한 후에, 명시적으로 @Execution(ExecutionMode.CONCURRENT)
를 지정한다.
package com.devkuma.junit5.parallel;
import org.junit.jupiter.api.*;
class ParallelTest3 {
@Nested
class StandardNestedTest {
// ...
}
@Nested
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Execution(ExecutionMode.CONCURRENT)
class PerClassTest {
// ...
}
@Nested
@TestMethodOrder(MethodOrderer.Alphanumeric.class)
@Execution(ExecutionMode.CONCURRENT)
class OrderedTest {
// ...
}
private static void printThread(String test) {...}
}
실행 결과:
PerClassTest.test2()@ForkJoinPool-1-worker-23
StandardNestedTest.test2()@ForkJoinPool-1-worker-9
StandardNestedTest.test1()@ForkJoinPool-1-worker-27
OrderedTest.test2()@ForkJoinPool-1-worker-5
PerClassTest.test1()@ForkJoinPool-1-worker-31
OrderedTest.test1()@ForkJoinPool-1-worker-13
최상위 클래스의 기본 실행 모드 변경
설정 파일에 junit.jupiter.execution.parallel.mode.classes.default=concurrent
으로 지정하면 최상위 클래스의 기본 실행 모드만 변경이 된다.
/test/resource/junit-platform.properties
junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.classes.default=concurrent
앞에서설정했던 junit.jupiter.execution.parallel.mode.default=concurrent
것은 메소드 레벨의 병렬 실행을 제어하는 것으로, 이번에는 그 설정을 넣지 않았다.
따라서, 기본으로 same_thread
를 지정하고 있는 것과 같은 상태가 된다.
두개의 테스트 클래스를 생성하고, 두 클래스를 한번에 실행해 보도록 하자.
package com.devkuma.junit5.parallel;
import org.junit.jupiter.api.Test;
public class ParallelFooTest {
@Test
void test1() {
printThread("ParallelFooTest.test1()");
}
@Test
void test2() {
printThread("ParallelFooTest.test2()");
}
private static void printThread(String test) {
String name = Thread.currentThread().getName();
System.out.printf("%s@%s%n", test, name);
}
}
package com.devkuma.junit5.parallel;
import org.junit.jupiter.api.Test;
public class ParallelBarTest {
@Test
void test1() {
printThread("ParallelBarTest.test1()");
}
@Test
void test2() {
printThread("ParallelBarTest.test2()");
}
private static void printThread(String test) {
String name = Thread.currentThread().getName();
System.out.printf("%s@%s%n", test, name);
}
}
실행 결과:
ParallelFooTest.test2()@ForkJoinPool-1-worker-19
ParallelBarTest.test1()@ForkJoinPool-1-worker-5
ParallelFooTest.test1()@ForkJoinPool-1-worker-19
ParallelBarTest.test2()@ForkJoinPool-1-worker-5
위에 결과를 보면 테스트 클래스 단위로 다른 thread 로 실행되었고, 테스트 클래스내의 메소드는 단일 thread로 순차 실행되었다.
ParallelFooTest
,ParallelBarTest
는 다른 스레드(ForkJoinPool-1-worker-19
,ForkJoinPool-1-worker-5
)에서 실행되었다.- 그러나
ParallelFooTest
내에 각 메소드는 하나의 스레드(ForkJoinPool-1-worker-19
)에서 실행되었다.
설정별 Thread 실행
junit.jupiter.execution.parallel.mode.default
와 junit.jupiter.execution.parallel.mode.classes.default
설정의 조합은 전부 4가지 있고 각각 다음과 같은 동작이 된다.
클래스, 메소드 단일 스레드로 차례로 실행
junit.jupiter.execution.parallel.mode.default=same_thread
junit.jupiter.execution.parallel.mode.classes.default=same_thread
클래스별로 병렬 실행, 클래스 내의 메소드 차례로 실행
junit.jupiter.execution.parallel.mode.default=same_thread
junit.jupiter.execution.parallel.mode.classes.default=concurrent
junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.classes.default=concurrent
클래스 내의 메소드는 병렬 실행, 클래스는 차례로 실행
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.mode.classes.default=same_thread
클래스, 메소드 전부 병렬 실행
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.mode.classes.default=concurrent
동시 병렬 수 변경
프로세서(코어) 수에 따라 동시 병렬 수를 동적으로 변경
설정 구성 파일에서 junit.jupiter.execution.parallel.config.dynamic.factor
를 지정하여 병렬로 실행되는 스레드 수를 조정할 수 있다.
/test/resource/junit-platform.properties
jjunit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.config.strategy=dynamic
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.config.dynamic.factor=2
아래 클래스는 동시 실행 중인 스레드 수를 계산하는 클래스이다.
package com.devkuma.junit5.parallel;
import java.util.concurrent.atomic.AtomicInteger;
public class ParallelismCounter {
private final AtomicInteger counter = new AtomicInteger(0);
private final AtomicInteger max = new AtomicInteger(0);
public void increment() {
this.max.set(Math.max(this.max.get(), this.counter.incrementAndGet()));
}
public void decrement() {
this.counter.decrementAndGet();
}
public int getMaxCount() {
return this.max.get();
}
}
아래 테스트 코드는 동적 테스트 메커니즘을 사용하여 처리가 1초가 걸리는 테스트를 20개 실행되도록 하고, 테스트가 끝난 후(@AfterEach
)에 다양한 정보가 출력하게 된다.
package com.devkuma.junit5.parallel;
import org.junit.jupiter.api.*;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class ParallelismTest {
private long begin;
private ParallelismCounter counter = new ParallelismCounter();
@BeforeEach
void beforeEach() {
begin = System.currentTimeMillis();
}
@TestFactory
Stream<DynamicNode> testFactory() {
return IntStream
.range(0, 20)
.mapToObj(i -> DynamicTest.dynamicTest("test" + i, () -> {
counter.increment();
Thread.sleep(1000);
counter.decrement();
}));
}
@AfterEach
void printThreadNames() {
System.out.println(System.currentTimeMillis() - begin + "ms");
System.out.println("Available Processors = " + Runtime.getRuntime().availableProcessors());
System.out.println("Max Parallelism Count = " + counter.getMaxCount());
System.out.println("Active Thread Count = " + Thread.activeCount());
Thread[] activeThreads = new Thread[Thread.activeCount()];
Thread.enumerate(activeThreads);
IntStream.range(0, activeThreads.length)
.mapToObj(i -> "[" + i + "] " + activeThreads[i].getName())
.forEach(System.out::println);
}
}
실행 결과:
1023ms
Available Processors = 10
Max Parallelism Count = 20
Active Thread Count = 25
[0] Test worker
[1] /127.0.0.1:49609 to /127.0.0.1:49608 workers
[2] /127.0.0.1:49609 to /127.0.0.1:49608 workers Thread 2
[3] /127.0.0.1:49609 to /127.0.0.1:49608 workers Thread 3
[4] ForkJoinPool-1-worker-51
[5] ForkJoinPool-1-worker-37
[6] ForkJoinPool-1-worker-23
[7] ForkJoinPool-1-worker-9
[8] ForkJoinPool-1-worker-59
[9] ForkJoinPool-1-worker-45
[10] ForkJoinPool-1-worker-31
[11] ForkJoinPool-1-worker-17
[12] ForkJoinPool-1-worker-3
[13] ForkJoinPool-1-worker-53
[14] ForkJoinPool-1-worker-39
[15] ForkJoinPool-1-worker-25
[16] ForkJoinPool-1-worker-11
[17] ForkJoinPool-1-worker-61
[18] ForkJoinPool-1-worker-47
[19] ForkJoinPool-1-worker-33
[20] ForkJoinPool-1-worker-19
[21] ForkJoinPool-1-worker-5
[22] ForkJoinPool-1-worker-55
[23] ForkJoinPool-1-worker-41
[24] ForkJoinPool-1-worker-27
factor
에 지정한 값에 실행 환경의 프로세서 수(코어 수)를 곱한 수가, 병렬 실행되는 thread 수가 된다.- 테스트를 진행한 컴퓨터의 코어 수는 10이고,
factor
를 2로 지정하였으므로, 병렬로 실행되는 thread 수는 20이 된다. - 그리고, 병렬 실행의 실행에는 ForkJoinPool이 사용되고 있어, 풀 되는 thread 수는 병렬 실행되는 thread수 보다 많아지는 일이 있다 (Active thread 수가 병렬 실행 수보다 많다.)
- 테스트를 진행한 컴퓨터의 코어 수는 10이고,
factor
를 지정하지 않으면 기본값은 1이다.
factor
를 소수로 지정
그러고, factor
은 내부에서 BigDecimal
를 사용하기에, 소수를 지정할 수도 있다.
- thread 수는 구하고 발생한 수에서 소수점 이하는 버림(마지막으로
BigDecimal.intValue()
를 사용하여int
로 변환된다).
/test/resource/junit-platform.properties
junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.config.strategy=dynamic
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.config.dynamic.factor=0.5
실행 결과:
7063ms
Available Processors = 10
Max Parallelism Count = 3
Active Thread Count = 7
[0] Test worker
[1] /127.0.0.1:50150 to /127.0.0.1:50149 workers
[2] /127.0.0.1:50150 to /127.0.0.1:50149 workers Thread 2
[3] /127.0.0.1:50150 to /127.0.0.1:50149 workers Thread 3
[4] ForkJoinPool-1-worker-3
[5] ForkJoinPool-1-worker-5
[6] ForkJoinPool-1-worker-7
테스트를 진행한 컴퓨터의 코어 수는 10이고, factor
를 0.5로 지정하였으므로, 계산해 보면 병렬로 실행되는 thread 수는 5가 된다.
동시 병렬 수를 고정 값으로 설정
junit.jupiter.execution.parallel.config.strategy
로 fixed지정하면 고정 병렬 수를 지정할 수 있다.
병렬 수는 junit.jupiter.execution.parallel.config.fixed.parallelism
로 지정한다.
/test/resource/junit-platform.properties
junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.config.strategy=fixed
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.config.fixed.parallelism=6
테스트 코드 구현은 dynamic을 지정했을 때와 동일하다.
실행 결과:
4046ms
Available Processors = 10
Max Parallelism Count = 6
Active Thread Count = 10
[0] Test worker
[1] /127.0.0.1:50251 to /127.0.0.1:50250 workers
[2] /127.0.0.1:50251 to /127.0.0.1:50250 workers Thread 2
[3] /127.0.0.1:50251 to /127.0.0.1:50250 workers Thread 3
[4] ForkJoinPool-1-worker-3
[5] ForkJoinPool-1-worker-5
[6] ForkJoinPool-1-worker-7
[7] ForkJoinPool-1-worker-9
[8] ForkJoinPool-1-worker-11
[9] ForkJoinPool-1-worker-13
### 동시 병렬 수를 임의로 사용자 정의
junit-jupiter-engine
는 디폴트으로 런타임만 참조할 수 있으므로, 컴파일시도 참조할 수 있도록(testImplementation
) 지정한다.
build.gradle
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
//testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}
ParallelExecutionConfigurationStrategy
구현한 클래스 생성하다.
package com.devkuma.junit5.parallel;
import org.junit.platform.engine.ConfigurationParameters;
import org.junit.platform.engine.support.hierarchical.ParallelExecutionConfiguration;
import org.junit.platform.engine.support.hierarchical.ParallelExecutionConfigurationStrategy;
public class MyParallelExecutionConfigurationStrategy implements ParallelExecutionConfigurationStrategy {
@Override
public ParallelExecutionConfiguration createConfiguration(ConfigurationParameters configurationParameters) {
return new ParallelExecutionConfiguration() {
@Override
public int getParallelism() {
return 7;
}
@Override
public int getMinimumRunnable() {
return 7;
}
@Override
public int getMaxPoolSize() {
return 7;
}
@Override
public int getCorePoolSize() {
return 7;
}
@Override
public int getKeepAliveSeconds() {
return 30;
}
};
}
}
createConfiguration()
메서드에서ParallelExecutionConfiguration
구현한 인스턴스를 반환한다.ParallelExecutionConfiguration
에 정의된Getter
는ForkJoinPool
을 생성 시 사용된다.- 각 값의 의미는 ForkJoinPool의 생성자 Javadoc 참조
/test/resource/junit-platform.properties
junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.config.strategy=custom
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.config.custom.class=sample.junit5.MyParallelExecutionConfigurationStrategy
junit.jupiter.execution.parallel.config.strategy
에 custom 지정junit.jupiter.execution.parallel.config.custom.class
에서 새로 만든ParallelExecutionConfigurationStrategy
구현 클래스을 지정한다.
테스트 코드 구현은 dynamic을 지정했을 때와 동일하다.
실행 결과:
3043ms
Available Processors = 10
Max Parallelism Count = 7
Active Thread Count = 11
[0] Test worker
[1] /127.0.0.1:50725 to /127.0.0.1:50724 workers
[2] /127.0.0.1:50725 to /127.0.0.1:50724 workers Thread 2
[3] /127.0.0.1:50725 to /127.0.0.1:50724 workers Thread 3
[4] ForkJoinPool-1-worker-3
[5] ForkJoinPool-1-worker-5
[6] ForkJoinPool-1-worker-7
[7] ForkJoinPool-1-worker-9
[8] ForkJoinPool-1-worker-11
[9] ForkJoinPool-1-worker-13
[10] ForkJoinPool-1-worker-15
MyParallelExecutionConfigurationStrategy
에서 반환 된ParallelExecutionConfiguration
설정에 따라 병렬로 실행되는 것을 확인할 수 있다.
배타적 제어
배타적 제어(Exclusive control)란? 공유 자원에 대한 다중 액세스가 동시에 발생해도 문제 없이 동작할 수 있는 제어를 말한다.
배타적 제어가 없는 경우
클래스를 병렬로 실행하여 static
변수 n
를 세 가지 테스트 방법에서 10,000번 증가시킨다.
package com.devkuma.junit5.parallel;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
public class ExclusiveControlTest {
static int n = 0;
@Test
void test1() throws Exception {
process("test1");
}
@Test
void test2() throws Exception {
process("test2");
}
@Test
void test3() throws Exception {
process("test3");
}
private void process(String name) {
System.out.println("begin " + name);
for (int i = 0; i < 10000; i++) {
n++;
}
System.out.println("end " + name);
}
@AfterAll
static void afterAll() {
System.out.println("n = " + n);
}
}
실행 결과:
begin test3
begin test2
begin test1
end test3
end test2
end test1
n = 15978
당연히 동기화가 없으므로 결과는 30,000이 아니다.
배타적 제어를 넣은 경우
방법은 간단하다 각 테스트 메소드에 @ResourceLock
어노테이션을 설정한다.
package com.devkuma.junit5.parallel;
...
public class ExclusiveControlTest {
static int n = 0;
@Test
@ResourceLock("lock")
void test1() throws Exception { ... }
@Test
@ResourceLock("lock")
void test2() throws Exception { ... }
@Test
@ResourceLock("lock")
void test3() throws Exception { ... }
private void process(String name) { ... }
@AfterAll
static void afterAll() { ... }
}
실행 결과:
begin test1
end test1
begin test2
end test2
begin test3
end test3
n = 30000
- 각 메소드의 실행으로 동기화가 되어,
n
의 값은 30,000 이 되었다. @ResourceLock
을 클래스나 메소드로 설정하면, 그 안의 테스트 케이스는 동기를 실행하게 된다.value
에는 배타적 제어하기 위한 키가 되는 문자열을 지정한다.- 동일한 키 문자열이 설정된
@ResourceLock
에 대해서만 동기화가 발생한다.
- 동일한 키 문자열이 설정된
액세스 모드 지정
package com.devkuma.junit5.parallel;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.junit.jupiter.api.parallel.ResourceAccessMode;
import org.junit.jupiter.api.parallel.ResourceLock;
@Execution(ExecutionMode.CONCURRENT)
public class ExclusiveControlTest2 {
@Test
@ResourceLock(value = "lock", mode = ResourceAccessMode.READ_WRITE)
void test1() throws Exception {
process("test1(READ_WRITE)");
}
@Test
@ResourceLock(value = "lock", mode = ResourceAccessMode.READ)
void test2() throws Exception {
process("test2(READ)");
}
@Test
@ResourceLock(value = "lock", mode = ResourceAccessMode.READ)
void test3() throws Exception {
process("test3(READ)");
}
private void process(String name) throws Exception {
System.out.println("begin " + name);
Thread.sleep(500);
System.out.println("end " + name);
}
}
실행 결과:
begin test3(READ)
begin test2(READ)
end test3(READ)
end test2(READ)
begin test1(READ_WRITE)
end test1(READ_WRITE)
@ResourceLock
의 mode
에 의해 액세스 모드를 지정할 수 있다.
- 액세스 모드는
ResourceAccessMode
에 정의된 상수로 지정한다.
각 액세스 모드에 의한 배타 제어의 조합은 다음과 같다.
선행하고 있는 처리 | 나중에 발생한 처리 | 나중에 발생한 처리는 |
---|---|---|
READ | READ | 실행 가능 |
READ | READ_WRITE | 대기 |
READ_WRITE | READ | 대기 |
READ_WRITE | READ_WRITE | 대기 |
READ
만 있다면 병렬 실행 가능하지만,READ_WRITE
가 있다면 배타적 제어된다.READ
는 데이터를 읽을 때만 업데이트하지 않는 테스트 방법으로 지정하고,READ_WRITE
는 데이터를 업데이트하는 테스트 메소드를 지정하는 방법이다.