인프런 커뮤니티 질문&답변

danaldanalcom3님의 프로필 이미지
danaldanalcom3

작성한 질문수

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발

주문 기능 테스트

Junit5 의 Assertions.fail 에 대해 질문이 있습니다.

작성

·

1.4K

0

@Test
public void 상품주문_재고수량초과() throws Exception {

//Given
Member member = createMember();
Item item = createBook("시골 JPA", 10000, 10); //이름, 가격, 재고

int orderCount = 11; //재고보다 많은 수량

//When
assertThrows(NotEnoughStockException.class, () -> {
orderService.order(member.getId(), item.getId(), orderCount);});

//Then
fail("재고 수량 부족 예외가 발생해야 한다.");
}

안녕하십니까. 김영한님의 강의를 열심히 수강중인 학생입니다.

Junit5에 대해 질문이 있어 글을 올립니다.

제가 Junit5를 배워보려고 강의 진행중 Junit4 대신 Junit5 를 써보았습니다.

그런데 Assertions.fail 때문에 위 코드가 자꾸 실패가 뜨더라고요.

제가 생각한 로직은 김영한님 강의에서 처럼 Assertions.assertThrows 안의 로직에서 예외를 던지면 fail까지 내려오지 않고 그대로 테스트가 성공으로 종료되며, 만약 예외를 던지지 않으면 fail까지 내려와 테스트 실패가 나오는 것이었습니다.

어떻게 코드를 수정하면 될까요?

------------------------------------------------------------------------------------------------------------------

설명이 부족한 것 같아 추가로 남깁니다.

위 코드는 재고보다 많은 수량이 입력됐을때 예외를 제대로 내뱉는지 확인하기 위한 테스트입니다.

만약 예외를 제대로 뱉었다면 김영한님 강의에서처럼 fail() 까지 안넘어가고 assertThrows 에서 테스트가 종료되고 성공으로 반환되어야 했습니다.

그런데 위 코드에서는 코드 진행이 fail까지 내려가고 그대로 실패가 뜨더라고요. 

Service, repository와 같은 기타 다른 연관 코드들은 김영한님 코드와 동일하게 작성하였으며, fail을 주석처리하고 위 테스트를 돌렸을 경우 성공처리가 됩니다.

상기 목적을 달성하려면 위 코드를 어떻게 수정하면 될까요?

답변 4

2

저도 같은 부분이 궁금해서 댓글답니다!

 

제가 이해한게 맞다면 JUnit5의 경우 NotEnoughStockException이 예측한 오류여서 테스트가 통과된 후에 fail("재고수량 부족~")으로 넘어가기 때문에  테스트가 fail된다는 말씀이 맞나요?

 

맞다면 도움되었습니다. 감사합니다:)

안녕하세요 김소젬님

네 fail() 은 테스트를 강제로 실패하게 합니다 :)

2

안녕하세요 danaldanalcom3 님!

.

Assertions.assertThrows() 에서 사용자가 예측한 오류가 던져질 경우엔 테스트 성공으로 처리되며 프로세스가 중단되지 않고 다음 프로세스를 진행하게 됩니다. 테스트가 실패하면 그 테스트는 종료되지만 테스트가 통과한 경우에는 테스트의 끝 (@Test가 붙은 함수의 끝) 까지 프로세스를 계속 진행하게 됩니다.

아래는 Assertions.assertThrows() 메서드를 추적하여 살펴본 실제 구현 코드 입니다. 사용자가 오류가 날 수 있다고 전제한 코드를 실제로 실행 한뒤, 클라이언트가 예측한 오류가 나는지를 확인하는 코드 입니다. 예측한 오류가 나지 않으면 검증 실패 오류를 발생시키고, 오류는 발생했지만 클라이언트가 예측한 오류가 아닌경우에 불일치 오류를 던지고 있습니다. 이러한 오류가 던져지면 테스트는 실패로 간주됩니다. 그러나 클라이언트가 예측한 오류가 맞다면 그 오류를 그대로 반환하여 다른 처리를 하게 되고 테스트는통과로 처리 됩니다.

private static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable,
			Object messageOrSupplier) {

		try {
			executable.execute();
		}
		catch (Throwable actualException) {
			if (expectedType.isInstance(actualException)) {
				return (T) actualException;
			}
			else {
				UnrecoverableExceptions.rethrowIfUnrecoverable(actualException);
				String message = buildPrefix(nullSafeGet(messageOrSupplier))
						+ format(expectedType, actualException.getClass(), "Unexpected exception type thrown");
				throw new AssertionFailedError(message, actualException);
			}
		}

		String message = buildPrefix(nullSafeGet(messageOrSupplier))
				+ String.format("Expected %s to be thrown, but nothing was thrown.", getCanonicalName(expectedType));
		throw new AssertionFailedError(message);
	}

감사합니다. 제가 무엇을 모르고 있는지 정확히 알겠네요!

2

danaldanalcom3님 제가 질문 내용을 잘못 이해했군요..

확인 해 본 결과, 최신 메뉴얼의 코드를 그대로 작성하셨다는 가정하에 

@RunWith 어노테이션 삭제,

@Test 어노테이션을 

import org.junit.jupiter.api.Test;

로 바꾸면 통과 되더라구요.

public이 생략된 것으로 Junit5가 적용된 것을 확인 하실 수 있으십니다.

0

안녕하세요. danaldanalcom3님, 공식 서포터즈 OMG입니다.

테스트 메서드 명을 보시다시피 "상품주문_재고수량초과()" 라고 작성하셨잖아요? 저라면

재고수량초과 -> assertThrows(NotEnoughStockException.class, () -> { orderService.order(member.getId(), item.getId(), orderCount);}); // 이 코드로 검증을 끝내고

--

그리고 재고수량이 초과하지 않았을 경우에 대한 테스트를 따로 만들어서 "재고수량이 몇개인데 몇개를 주문하니까 몇개가 남았다" 라는 검증을 할 것 같습니다. 하나의 테스트에는 하나의 핵심 로직 테스트를 작성하시되 케이스가 나뉘는 경우엔 각각에 대한 테스트를 작성하는게 좋은 것 같아요.

감사합니다.

아니요... 질문설명이 부족한지 답변자님께서 질문에 대해 잘못이해하신거 같습니다.

저 테스트는 상품주문시 재고수량이 초과 됐을때 에러를 제대로 던지는지 확인하는 것이 목적이었어요. 그래서 orderService.order(member.getId(), item.getId(), orderCount); 를 했을 때 예외를 던지고 그대로 끝나야 했습니다. 만약 예외를 던지지 않았을 경우 테스트가 실패로 끝나게 하기 위해서 fail 메서드를 사용한거에요.  그런데 위 코드는 재고수량이 초과되어서 예외를 던지고 테스트가 성공으로 끝나야하는데 fail까지 진행되버려서 테스트가 실패로 끝납니다. 즉, 원하는대로 코드가 작동하지 않는거죠. 그래서 질문을 올린겁니다.

danaldanalcom3님의 프로필 이미지
danaldanalcom3

작성한 질문수

질문하기