티스토리 뷰
회사에서 테스트 코드를 작성하던 도중 Exception 발생 시 catch 이후의 flow 를 검증해야 하는 상황이 발생했다.
Exception Testing에 사용한 방법은 ExpectedException Rule 을 사용하고 있었다. ExpectedException Rule 같은 경우, 발생할(expect) exception 에 대해서 작성해 둘 뿐만 아니라 예상되는 message까지 쉽게 선언 할 수 있어서 좋은 테스트 방식으로 생각하고 있었다.
매우 간단하게 익셉션이 발생하는 메소드 호출 후 Mockito verify 메소드를 호출하면 될 것으로 예상했는데 막상 테스트 코드를 작성 하고 실행해보니 예상과 다르게 동작했다.
이를 확인해 보고자 junit wiki를 확인해보니 ExpectedException Rule의 경우 테스트 중인 메소드가 예외를 던지면 이후의 테스트는 실행되지(유효하지) 않다는 내용이었다. 이러한 점은 사용자에게 혼동을 줄 수 있고 이 때문에 ExpectedException.none()은 deprecated되었다고 한다.
Exception testing · junit-team/junit4 Wiki
GitHub - junit-team/junit4: A programmer-oriented testing framework for Java.
A programmer-oriented testing framework for Java. Contribute to junit-team/junit4 development by creating an account on GitHub.
github.com
혼동을 줄 수 있다는 점은 Exception 이후의 예상되는 동작이 Test를 실행 한 후 Mockito verify에 의해 호출되었는가를 판단하기 전에 기대한 Exception 이 발생되어 Test가 Pass 되는 상황을 불러 올 수 있다.(Exception만 판단하기 때문에)
위에서 설명한 문제는 파일의 메타 데이터를 DB에 저장하고 파일 서버에 물리적인 파일을 저장하다가 실패하는 case에 DB를 rollback하는 테스트를 작성 중인 상황에서 발생했다.
Exception 발생시 DB를 rollback 하는 code를 아직 추가 하지 않은 상태로 테스트 코드만 먼저 작성해서 rollback code를 Mockito verify로 검증하는 테스트 코드를 작성해 두었는데, ExpectedException Rule에서는 Exception발생 여부만 확인하기 때문에 Test가 Pass되고 있었다. 따라서 ExpectedException Rule Testing 방식은 잘못 작성한 경우 다른 사용자에게 혼동을 줄 수 있고, Exception이후의 flow를 테스트 하기에는 적절하지 않은 테스트 방식이었다.
public void fileUpload(file) {
...
try {
fileDao.save(file metadata);
minioService.store(file);
} catch (IOException e) {
//fileDao.rollback(); //rollback 코드를 추가 하기 전
}
}
@Rule
public ExpectedException exception = ExpectedException.none();
...
@Test
public void testFileUpload() {
exception.expect(IOException.class);
exception.expectMessage("error message");
Mockito.doThrow(IOException.class).when(minioService).store(any());
fileService.fileUpload(file); //exception 발생
Mockito.Verify(fileDao).rollback(); // -> 실제 구현된 rollback 코드가 없기 때문에 Test false가 나올 것으로 예상되지만, Exception 검사만 하기때문에 Pass.
}
위에 예제 코드를 보면, 실제 rollback 코드는 아직 구현되지 않은 상태로 추가가 되어 있지 않은 상태에서 Mockito verfiy를 호출하면 테스트에 실패할 것으로 예상했다. 하지만 실제 실행했을 때는 fileUpload method의 minioService.store(file)에서 IOException이 발생하며 expect exception이 발생하기 때문에 Test는 pass로 완료된다.
따라서 exception 이후의 flow에 대한 검증을 하기위해서는 다른 방법을 사용해야 한다. @Test annotaion 을 이용한 방법도 마찬가지이다. (Try/Catch 문구, JUnit 4.13 부터는 assertThrows 방법도 사용 가능)
만약 꼭 ExpectedException Rule 방식을 사용해서 Exception 이후의 flow를 검증해야 한다면, Try/finally 를 사용해서 실행되게 할 수 있다.
@Test
public void testFileUpload() {
exception.expect(IOException.class);
exception.expectMessage("error message");
Mockito.doThrow(IOException.class).when(minioService).store(any());
try {
fileService.fileUpload(file); //Exception 발생
} finally {
Mockito.Verify(fileDao).rollback(); //finally 문구가 먼저 실행
}
}
하지만 위 방식은 의도되는 테스트 작성 방식과는 거리가 있어 보인다. 따라서 deprecated된 ExpectedException Rule 방식보다는 다른 Exception Testing 방법을 사용하는 것이 이후의 테스트 코드 관리를 위해서도 좋을 것 같다.
- Total
- Today
- Yesterday
- 웹 애플리케이션
- 특정 ip
- OneToOne
- Spring Cloud Stream
- WEB-INF
- 서버 클라이언트
- 데스크톱 애플리케이션
- ServiceMonitor
- Java 특징
- springboot3.x
- MySQL 외부 IP
- Java 란
- 애플리케이션 변화 과정
- cpus
- ExpectedException
- docker-compose
- Prometheus Operator
- Java 장단점
- Kafka
- minikube node add
- kubernetes
- node add
- StreamBridge
- producer
- minikube
- DD파일
- Servlet
- 애노테이션 프로세서
- consumer
- Servlet Container
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
