
Spring boot + JdbcTemplate 사용해보기 (3) Repository 구현 에서 구현한 Repository 를 테스트 하기 위해, 테스트 코드를 작성했다.
테스트 환경은 test/resources/application.properties 파일을 바라보고 있다.
아래와 같이 datasource 에 대한 설정을 해주었다. (SQL 확인을 위해 jdbc log 설정도 해주었다.)
spring.datasource.url=jdbc:mysql://localhost:3309/church?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true
spring.datasource.username=test
spring.datasource.password=test
#jdbcTemplate sql log
logging.level.org.springframework.jdbc=debug
그리고, 테스트 클래스를 아래와 같이 작성하였다.
@SpringBootTest 를 사용했으므로, 스프링 어플리케이션을 구동시킬때와 마찬가지로 스프링 컨테이너가 올라가서 Bean 이 등록된다.
MemberRepository 에는 구현체인 JdbcTemplateMemberRepository 가 주입되고, JdbcTemplate 는 스프링 어플리케이션이 구동되면서 자동 등록된 Bean 이 사용된다.
(참고: JdbcTemplateConfiguration 클래스를 통해 Bean 이 등록된다.)
각 테스트 케이스가 독립적으로 실행되도록 하기 위해서 afterEach 메서드에서 members 테이블을 truncate 해주었다.
@SpringBootTest
class MemberRepositoryTest {
@Autowired
MemberRepository memberRepository;
@Autowired
JdbcTemplate template;
@AfterEach
void afterEach() {
String sql = "TRUNCATE TABLE members";
template.update(sql);
}
// 나머지 생략
}
모든 케이스를 다 살펴볼 필요는 없을 것 같고, 몇 가지 기억하고 싶은 부분들만 기록한다.
Member 객체를 생성하여 repository 를 통해 save 한 뒤, repository 를 통해 find 했을 때 두 객체가 일치하는 지 확인하는 테스트를 작성했다.
@Test
void save() {
//given
Member member = Member.builder()
.name("Peter")
.gender(Gender.MALE)
.position("집사")
.birthDate(LocalDate.parse("1995-01-01"))
.address("서울시 강남구 학동로")
.phoneNumber("010-1111-2222")
.build();
//when
Member memberSave = memberRepository.save(member);
Member memberFound = memberRepository.findById(memberSave.getId()).get();
//then
assertThat(memberFound).isEqualTo(memberSave);
}
맨 하단에 assertThat(memberFound).isEqualTo(memberSave) 코드는 내부적으로 두 객체간의 equals 메서드를 호출하는데 Member 클래스에 @EqualsAndHashCode 롬복 어노테이션이 붙어 있어서 equals 메서드가 자동으로 만들어지기 때문에 제대로 동작하는 것이다.
자동 생성된 equals 메서드를 디컴파일 해보면, 모든 필드에 대한 동등비교가 통과될 때만 true 가 리턴되는 것을 확인할 수 있다.
(@EqualsAndHashCode 이 안붙어 있다면, 그냥 인스턴스 주소 비교이기 때문에 테스트 케이스가 fail 이 되어버린다.)
데이터가 없는 id 로 조회하는 경우에 예외가 발생하지 않고 empty 인 Optional<Member> 가 반환되어야 하는데, 해당 동작을 테스트했다.
@Test
void findByIdForNonExistingRow() {
//given
Long memberIdForNonExisting = 100000L; // NOTE: @AfterEach 에서 members table truncate 하는 로직이 있어야 완전한 non-existence 보장됨.
//when
Optional<Member> memberOptional = memberRepository.findById(memberIdForNonExisting);
//then
assertThat(memberOptional).isEmpty();
}
검색 조건에 맞는 Member 가 조회되는 지 테스트했다.
@Test
void findWithCond() {
//given
Member member1 = Member.builder()
.name("Peter")
.gender(Gender.MALE)
.position("집사")
.birthDate(LocalDate.parse("1995-01-01"))
.address("서울시 강남구 학동로")
.phoneNumber("010-1111-2222")
.build();
Member member2 = Member.builder()
.name("John")
.gender(Gender.MALE)
.position("집사")
.birthDate(LocalDate.parse("1995-01-01"))
.address("서울시 강남구 학동로")
.phoneNumber("010-1111-2222")
.build();
Member member3 = Member.builder()
.name("Paul")
.gender(Gender.MALE)
.position("집사")
.birthDate(LocalDate.parse("1995-01-01"))
.address("서울시 강남구 학동로")
.phoneNumber("010-1111-2222")
.build();
Member member4 = Member.builder()
.name("Grace")
.gender(Gender.MALE)
.position("집사")
.birthDate(LocalDate.parse("1995-01-01"))
.address("서울시 강남구 학동로")
.phoneNumber("010-1111-2222")
.build();
memberRepository.save(member1);
memberRepository.save(member2);
memberRepository.save(member3);
memberRepository.save(member4);
MemberSearchParam param = new MemberSearchParam("a");
//when
List<Member> list = memberRepository.findAll(param);
Set<String> setOfNames = list.stream().map(Member::getName).collect(Collectors.toSet());
//then
assertThat(list).hasSize(2);
assertThat(setOfNames).isEqualTo(Set.of("Grace","Paul"));
}
테스트 케이스 조건(given 절) 설정에 따라 조회된 Member 들의 이름 비교로 검증이 가능해서, Set 에 이름을 넣고 Set 에 대한 동등비교를 함으로써 assert 를 해보았다.
관심있는 것은 name 뿐인데, 다른 데이터들이 많아서 괜히 장황한 것 같다.
관심있는 데이터만 set 하고, 나머지는 fake 데이터를 랜덤하게 넣어주거나 하는 장치를 추가하면 좋을 것 같다.