개발

Spring Boot 3.x.x 버전에서 Integration Test

Developer Document 2023. 8. 21. 20:55

Reactive MongoDB Integration Test

최근 개인 프로젝트 하나를 진행하고 있는데, Webflux + Reactive MongoDB 조합으로 Integration 테스트 코드 작성한 방법을 공유하려고 한다.

구글링하면 DataMongoTest 어노테이션과 embedded MongoDB 라이브러리를 통해서 테스트하는 예시는 많지만, 내 프로젝트에서는 정상 동작하지 않았다.

내 프로젝트에서 사용하는 기술 스택과 라이브러리 버전부터 정리해보자.

기술 스택, 라이브러리 버전

  • Spring Boot 3.1.0
  • spring-boot-starter-webflux
  • reactor-test 3.5.4
  • spring-boot-starter-data-mongodb-reactive
  • de.flapdoodle.embed.mongo.spring30x

위 라이브러리 중 가장 중요한 것은 de.flapdoodle.embed.mongo.spring30x 이다.

대부분의 예시에서는 de.flapdoodle.embed.mongo를 많이 사용하는데, Spring Boot 3.0.0 이상부터는 위 라이브러리를 사용해야지 정상 동작한다.

application.yml

de.flapdoodle.embed.mongo.spring30x 라이브러리를 추가했다면 application.yml 파일을 수정해야한다.

properties 파일을 사용해도 동일하게 추가해야한다.

de:
  flapdoodle:
    mongodb:
      embedded:
        version: 5.0.5

version의 값은 MongoDB의 버전이다. 원하는 버전으로 입력하면 된다.

Entity

Integration 테스트에서 사용하기 위한 Entity를 하나 작성한다.

@Document(collection = "user")
@Getter
public class UserEntity {

    @Id
    private String userId;
    private String nickname;
    private String password;
    private String phoneNumber;
    private Boolean autoLogin;

    public UserEntity() {}

    @Builder
    public UserEntity(String userId, String nickname, String password, String phoneNumber, Boolean autoLogin) {
        this.userId = userId;
        this.nickname = nickname;
        this.password = password;
        this.phoneNumber = phoneNumber;
        this.autoLogin = autoLogin;
    }
}

중요한 것은 @Id 어노테이션을 쓸 때 org.springframework.data.annotation.Id를 import 해야한다.

다른 Id 라이브러리인 jakarta.persistence.Id를 사용한다면 이후 만들 Repository의 findById가 정상 동작하지 않는다.

Repository

이제 UserEntity를 persistence layer에서 사용하기 위해 Repository 인터페이스를 하나 정의한다.

interface UserRepository extends ReactiveMongoRepository<UserEntity, String> {
}

처음에 말했듯이 이번 프로젝트에서는 ReactiveMongoRepository를 사용하였다.

Test code

이제 작성한 내용을 바탕으로 테스트 코드를 작성해보자.

전체 코드는 다음과 같다.

@DataMongoTest()
@ExtendWith(SpringExtension.class)
public class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    private final String USER_ID = "user-id";

    @Test
    void saveUser_success() {
        var user = createUserEntity();

        StepVerifier.create(userRepository.deleteAll().then(userRepository.save(user)))
                .expectNextMatches(userEntity -> userEntity.getUserId().equals(user.getUserId())
                        && userEntity.getNickname().equals(user.getNickname())
                        && userEntity.getPassword().equals(user.getPassword())
                        && userEntity.getPhoneNumber().equals(user.getPhoneNumber())
                        && userEntity.getAutoLogin().equals(user.getAutoLogin()))
                .verifyComplete();
    }

    @Test
    void readUser_success() {
        var user = createUserEntity();

        Publisher<UserEntity> setup = userRepository.save(user);
        Mono<UserEntity> find = userRepository.findById(USER_ID);

        Publisher<UserEntity> composite = Mono.from(setup).then(find);

        StepVerifier.create(composite)
                .assertNext(userEntity -> {
                    assertEquals(userEntity.getNickname(), user.getNickname());
                })
                .verifyComplete();
    }

    private UserEntity createUserEntity() {
        return UserEntity.builder()
                .userId(USER_ID)
                .nickname("nickname")
                .password("password")
                .phoneNumber("phone-number")
                .autoLogin(true)
                .build();
    }
}

마무리

사실 테스트 코드는 볼만한 부분이 없다.

중요한 것은 Spring boot 3.0.0 이상에서는 de.flapdoodle.embed.mongo.spring30x 라이브러리를 쓴다는 것이 중요하다.

이 부분을 몰라서 삽질을 하루 정도 했기 때문에 혹시나 이 글을 본다면 도움이 되기를 바란다.