Mockito를 이용한 테스트 코드 작성시 발생한 이슈를 간단히 정리하고자 합니다.
@Table(name = "category")
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Category extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "parent_id")
private Long parentId;
@Builder
public Category(String name, Long parentId) {
this.name = name;
this.parentId = parentId;
}
}
- 제가 보통 쓰는 엔티티 코드방식입니다.
- 클래스 상단에 @Builder를 선언하면 IDENTITY전략을 설정한 기본키 값에 값을 할당할 수 있기에, 안전하지 않습니다.
- 따라서, 생성자에 @Builder를 선언하여 엔티티 생성시 필요한 데이터만 받을 수 있도록 작성하였습니다.
- 추가적으로 엔티티에는 @Setter 메소드를 선언하면 좋지 않습니다. 엔티티 혹은 객체는 불변한 성질을 가져야 하기 때문입니다. 만약 중간에 변경될 요지를 준다면 추후 코드추적하기가 어렵습니다.
@ExtendWith(MockitoExtension.class)
class CategoryServiceTest {
@Mock
private CategoryRepository categoryRepository;
@Test
@DisplayName("카테고리 조회")
void categoryOneDepthSelect() {
given(categoryRepository.findAll()).willReturn(getStubCatogories());
CategoryService categoryService = new CategoryService(categoryRepository);
CategoryDto categoryRoot = categoryService.categoryRoot();
System.out.println(categoryRoot);
assertThat(categoryRoot.getSubCategories().size()).isEqualTo(2);
assertThat(categoryRoot.getSubCategories().get(0).getSubCategories().size()).isEqualTo(2);
assertThat(categoryRoot.getSubCategories().get(1).getSubCategories().size()).isEqualTo(2);
}
}
- 목데이터로 테스트를 하기 위해 Mockito를 사용하였습니다.
- 단위 테스트에 대한 자세한 내용은 아래 게시글을 참조 바랍니다
private List<Category> getStubCatogories() {
Category sub1 = new Category("SUB1", 0L);
Category sub2 = new Category("SUB2", 0L);
Category sub11 = new Category("SUB1-1", 1L);
Category sub12 = new Category("SUB1-2", 1L);
Category sub21 = new Category("SUB2-1", 2L);
Category sub22 = new Category("SUB2-2", 2L);
ReflectionTestUtils.setField(sub1, "id", 1L);
ReflectionTestUtils.setField(sub2, "id", 2L);
ReflectionTestUtils.setField(sub11, "id", 3L);
ReflectionTestUtils.setField(sub12, "id", 4L);
ReflectionTestUtils.setField(sub21, "id", 5L);
ReflectionTestUtils.setField(sub22, "id", 6L);
List<Category> categories = List.of(
sub1, sub2, sub11, sub12, sub21, sub22
);
return categories;
}
- 카테고리 리스트에 대한 더미 데이터를 만들어야 했습니다.
- 통합 테스트를 한다면, CategoryRepository의 findAll 메소드를 통해 DB에 존재하는 카테고리 데이터를 읽어와 사용할 수 있습니다.
- 하지만, 단위테스트를 사용해야했기에 Category 엔티티를 직접 만들어야 했습니다.
- Category 엔티티에서 Id 필드는 private 타입, setter 메소드 존재 X, 생성자 필드 X 하기 때문에 id 필드를 주입해야 했습니다.
- ReflectionTestUtils를 이용하여 private 필드값에 직접 할당 할 수 있습니다.
- spring-context 5.1.2 / spring-test 5.1.2 모듈이 필요합니다. 최신 스프링부트 버전을 사용하신다면 바로 사용하실 수 있습니다.
@Test
public void testRead1(){
List<Category> stubCatogories = getStubCatogories();
stubCatogories.forEach(category -> {
System.out.println(category.getId());
});
}
- 만든 더미데이터를 출력해보면 정상적으로 직접 세팅한 id값을 확인할 수 있습니다.
참고
www.baeldung.com/spring-reflection-test-utils
'Tech > Spring' 카테고리의 다른 글
Mybatis와 Jpa 사용시 트랜잭션 묶어서 사용하는 방법(※ 멀티 datasource 설정 / QueryDsl) (0) | 2021.07.24 |
---|---|
Spring Filter에서 request Body 가공하기 (0) | 2021.07.19 |
Springboot + GCP Cloud Storage 연동(파일 업로드, 다운로드) (0) | 2021.03.22 |
Spring Boot에서 Actuator 및 Spring Actuator Admin 설정 방법 (0) | 2021.03.21 |
Google 소셜 로그인 Google API 클라이언트 라이브러리 + GoogleIdTokenVerifier 사용하는 방법 (4) | 2021.03.15 |