Spring boot + JdbcTemplate 사용해보기 (4) RepositoryTest 작성

Peter·2024년 7월 18일
0
post-thumbnail

Spring boot + JdbcTemplate 사용해보기 (3) Repository 구현 에서 구현한 Repository 를 테스트 하기 위해, 테스트 코드를 작성했다.

의존성 설정 및 DB clear

테스트 환경은 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);
    }
    
    // 나머지 생략
}

상세 테스트 케이스

모든 케이스를 다 살펴볼 필요는 없을 것 같고, 몇 가지 기억하고 싶은 부분들만 기록한다.

save 테스트

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 이 되어버린다.)

findById 테스트

데이터가 없는 id 로 조회하는 경우에 예외가 발생하지 않고 emptyOptional<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();
}

findAll 테스트

검색 조건에 맞는 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 데이터를 랜덤하게 넣어주거나 하는 장치를 추가하면 좋을 것 같다.

참고

0개의 댓글