[Spring] JUnit과 AssertJ로 테스트 코드 작성하기
Spring에서 주로 사용하는 JUnit과 AssertJ를 왜, 어떻게 사용하는 지 예시 코드를 통해 살펴보자.
Apr 20, 2024
✅ 들어가기 전에
테스트 코드를 작성하는 이유가 무엇일까?
개발 단계에서 테스트 코드를 작성하는 이유는 정말 다양할 것이다. 그 중에서도 가장 손꼽히는 이유는:
- 개발 과정에서 문제를 미리 예측 및 발견할 수 있다.
- 애플리케이션을 직접 가동해서 테스트하는 것보다 테스트를 빠르게 진행할 수 있다.
- 단위 테스트를 통해 각각의 기능 별 테스트가 가능하다.
- 코드가 작성된 목적을 명확하게 표현할 수 있으며, 불필요한 내용이 추가되는 것을 방지한다.
- 버그를 미리 발견하여 리팩토링의 리스크가 줄어든다.
스프링 부트는 애플리케이션을 테스트하기 위한 도구와 어노테이션을 제공한다. 아래와 같은 여러 테스트 도구가
spring-boot-starter-test
에 들어있다.JUnit
- 자바 프로그래밍 언어용 단위 테스트 프레임워크
Spring Test & Spring Boot Test
- 스프링 부트 애플리케이션을 위한 통합 테스트 지원
AssertJ
- 검증문인 어설션을 작성하는 데 사용
Hamcrest
- 표현식을 보다 이해하기 쉽게 만드는 데 사용되는 Matcher 라이브러리
Mockito
- 테스트에 사용할 가짜 객체인 mock 객체를 쉽게 만들고, 관리하고, 검증할 수 있게 지원하는 테스트 프레임워크
JSONassert
- JSON 용 어설션 라이브러리
JsonPath
- JSON 데이터에서 특정 데이터를 선택하고 검색하기 위한 라이브러리
→ 이 중에서 JUnit과 AssertJ를 가장 많이 사용한다!
✅ JUnit이란?
JUnit은 자바 언어를 위한 단위 테스트 프레임워크이다. 단위 테스트란, 작성한 코드가 나의 의도에 맞게 동작하는 지 작은 단위로 검증하는 것을 말한다(이때 보통 단위는 하나의 메소드를 의미한다).
JUnit의 특징
@Test
어노테이션으로 메소드를 호출할 때마다 새로운 인스턴스를 생성한다.
- 예상 결과를 검증하는
Assertion
메소드를 제공한다.
- 사용법이 단순하다.
- 자동으로 실행하고, 자체적으로 결과를 확인해 피드백을 제공한다.
코드 예시
@Test @DisplayName("상품 1개 등록하기 - TestRestTemplate 기반") void createProduct() { // given Long adminId = 1L; String name = "보조배터리"; Long price = 10000L; String category = "전자기기"; String thumbnailUrl = "http://abc.com"; ProductRequestMvc productRequestMvc = ProductRequestMvc.builder() .adminId(adminId) .name(name) .price(price) .category(category) .thumbnailUrl(thumbnailUrl) .build(); String url = "http://localhost:" + port + "/mvc/product/create"; // API url 주소 // when ResponseEntity<ProductResponseMvc> responseEntity = testRestTemplate.postForEntity(url, productRequestMvc, ProductResponseMvc.class); // then assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK); ProductEntityMvc savedProduct = productServiceMvc.findById(responseEntity.getBody().getId()); Assertions.assertEquals(savedProduct.getAdminId(), adminId); Assertions.assertEquals(savedProduct.getName(), name); Assertions.assertEquals(savedProduct.getPrice(), price); Assertions.assertEquals(savedProduct.getCategory(), category); Assertions.assertEquals(savedProduct.getThumbnailUrl(), thumbnailUrl); }
Given-When-Then Pattern
코드를 자세히 살펴보았다면, given, when, then이라 주석처리 되어 있는 부분을 발견했을 것이다. given, when, then이 무엇일까? 이것은 테스트 코드를 작성할 때 흔히 사용하는 패턴이다. 테스트 진행 프로세스를 3가지로 나누어 각각에 맞는 작업을 수행하는 것이다.
given
: 테스트 하기 위해 기본적으로 세팅하는 절차이다.
when
: 테스트를 하기 위한 조건을 지정한다.
then
: 테스트 하기 위한 행위와 결과값이 우리가 예상하는대로 잘 수행되는지 검증한다.
이렇게 3가지 부분으로 나누어 테스트를 하기 위한 목적이 무엇인지 명확히 할 수 있다. 위 예시 코드에서 각각 수행하는 역할은 아래와 같다.
- given
- 상품 객체를 하나 생성한다.
- when
- testRestTemplate을 통해 상품 등록 API를 실행한다.
- then
- 상품이 잘 저장되었는지 서비스를 통해 가져와서 속성을 비교하며 확인한다.
Assertions.assertEquals(savedProduct.getAdminId(), adminId);
에서savedProduct.getAdminId()
는 검증하고 싶은 값을 의미하고,adminId
는 실제 비교할 값을 의미한다.
위와 같은 상품을 등록하는 간단한 코드를 JUnit을 통해 작성할 수 있다.
✅ AssertJ로 검증문 수정하기
AssertJ는 JUnit과 함께 사용하여 검증문의 가독성을 높여주는 라이브러리이다. 위 테스트 코드의 Assertions를 통한 검증문은 기댓값과 실제 비교값을 명시하지 않으므로 비교 대상이 헷갈릴 수 있다.
이를 해결하기 위해 AssertJ를 사용하여 코드를 아래와 같이 수정할 수 있다.
Assertions.assertEquals(savedProduct.getAdminId(), adminId);
assertThat(savedProduct.getAdminId()).isEqualTo(adminId);
위처럼
assertThat()
을 사용하면, 검증할 값과 실제 비교 대상을 명확하게 표현할 수 있다. 따라서, 수정한 최종 코드는 아래와 같다. @Test @DisplayName("상품 1개 등록 - TestRestTemplate 기반") void createProduct() { // given Long adminId = 1L; String name = "보조배터리"; Long price = 10000L; String category = "전자기기"; String thumbnailUrl = "http://abc.com"; ProductRequestMvc productRequestMvc = ProductRequestMvc.builder() .adminId(adminId) .name(name) .price(price) .category(category) .thumbnailUrl(thumbnailUrl) .build(); String url = "http://localhost:" + port + "/mvc/product/create"; // when ResponseEntity<ProductResponseMvc> responseEntity = testRestTemplate.postForEntity(url, productRequestMvc, ProductResponseMvc.class); // then assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK); ProductEntityMvc savedProduct = productServiceMvc.findById(responseEntity.getBody().getId()); assertThat(savedProduct.getAdminId()).isEqualTo(adminId); assertThat(savedProduct.getName()).isEqualTo(name); assertThat(savedProduct.getPrice()).isEqualTo(price); assertThat(savedProduct.getCategory()).isEqualTo(category); assertThat(savedProduct.getThumbnailUrl()).isEqualTo(thumbnailUrl); }
위에서 사용한
isEqualTo()
는 값이 같은지 검증하는 코드이다. 이 외에도 아래와 같이 여러가지 다양한 검증 메소드가 존재한다.메소드 | 설명 |
isEqualTo(X) | X 값과 같은지 검증 |
isNotEqualTo(X) | X 값과 다른지 검증 |
contains(X) | X 값을 포함하는지 검증 |
doesNotContain(X) | X 값을 포함하지 않는지 검증 |
startsWith(X) | 접두사가 X인지 검증 |
endsWith(X) | 접미사가 X인지 검증 |
isEmpty(X) | 비어있는 값인지 검증 |
isNotEmpty() | 비어있지 않은 값인지 검증 |
isPositive() | 양수인지 검증 |
isNegative() | 음수인지 검증 |
isGreaterThan(N) | N보다 큰 값인지 검증 |
isLessThan(N) | N보다 작은 값인지 검증 |
추후에
Mock
, TestRestTemplate
등에 대해서도 알아보자!Share article