테스트 코드의 가치가 널리 인정받으며, 이제 그 필요성을 언급하는 것은 의미가 없어졌습니다. 테스트 코드의 가장 큰 매력은 바로 구현 코드에 대한 실시간 피드백을 제공하고, 이를 바탕으로 구현 코드를 지속적으로 개선해 나갈 수 있다는 점입니다. 이 개념은 "실무에서 적용하는 테스트 코드 작성 방법과 노하우 Part 2: 테스트 코드로부터 피드백 받기"사내 기술 블로그에 이미 다룬 바 있습니다. 폭넓은 테스트를 작성하고 실행하기 위해서는 테스트 코드의 간편한 작성이 필수적입니다. 본 포스팅에서는 Spring Data MongoDB를 사용하여 Given 절의 데이터 셋업을 쉽게 하는 방법을 소개하겠습니다.
데이터 셋업의 어려움과 중요성
위 이미지처럼 주문 테스트 코드에 대한 다양한 테스트 케이스를 작성하기 위해서는 다양한 데이터를 셋업 하는 것은 필수적입니다. 다양한 테스트 케이스를 작성하지 못하면 테스트 케이스가 커버하는 범위가 좁아지며, 이로 인해 테스트 코드로부터 양질의 피드백을 받을 수 없게 됩니다. 따라서, 테스트 코드를 쉽게 작성하고 다양한 시나리오를 손쉽게 검증할 수 있는 환경을 만드는 것이 중요합니다.
스프링에서는 @Sql 어노테이션을 이용해 테스트 데이터를 간단히 셋업 할 수 있으며, 이를 통해 테스트 케이스를 원활하게 확장할 수 있습니다. 이에 관한 자세한 방법은 Sql을 통해서 테스트 코드를 쉽게 작성하자" 포스팅에서 설명하고 있습니다. 해당 포스팅에서는 Sql을 활용하여 다양한 테스트 데이터를 쉽게 구성하는 방법을 제공합니다.
(1): @MongoTestSupport 설정을 통해서 테스트 실행 리스너로 추가하여 데이터 설정을 자동화합니다.
(2): @MongoDataSetup은 해당 JSON 파일일을 읽어 MongoDB에 삽입합니다.
(3): jsonPath은 test/resources/ 디렉토리에 위치한 JSON 파일의 경로를 지정합니다. 마지막으로,
(4): MongoDB에 삽입할 문서의 클래스를 명시합니다.
@MongoDataSetup 어노테이션을 사용하면, JSON 파일을 통해 MongoDB 테스트 데이터를 간편하게 설정할 수 있으며, 테스트 실행 시 Foo Document 객체가 성공적으로 저장되어 조회되는 것을 확인할 수 있습니다.
테스트 데이터 셋업 코드
MongoDataSetup
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/** * @param jsonPath json file의 경로를 작성한다. * @param clazz MongoDB에 저장할 Document 객체 * @param collectionName [clazz]와 Document collection 이름이 다른 경우 명시, 명시하지 않는 경우 [clazz]의 Document의 collection 으로 저장 * * @see com.example.mongostudy.MongoDataSetupExecutionListenerTest.mongoDataSetup * @see com.example.mongostudy.MongoDataSetupExecutionListenerTest.mongoDataSetupCollectionName */ @Target(AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) annotationclassMongoDataSetup( val jsonPath: String, val clazz: KClass<*>, val collectionName: String = "" )
단일 문서 데이터 셋업에 사용됩니다.
1 2 3 4 5 6 7 8 9
/** * 여러 데이터를 셋업이 필요한 경우 * @param mongoDataSetup * * @see com.example.mongostudy.MongoDataSetupExecutionListenerTest.mongoDataSetups */ @Target(AnnotationTarget.FUNCTION, AnnotationTarget.ANNOTATION_CLASS) @Retention(AnnotationRetention.RUNTIME) annotationclassMongoDataSetups(varargval mongoDataSetup: MongoDataSetup)
/** * 테스트 이전에 [MongoDataSetup]기반으로 Document를 생성한다. */ overridefunbeforeTestMethod(testContext: TestContext) { val currentTestMethod = testContext.testMethod val mongoDataSetup = currentTestMethod.getAnnotation(MongoDataSetup::class.java) val mongoDataSetups = currentTestMethod.getAnnotation(MongoDataSetups::class.java) when { mongoDataSetup != null -> insertDocuments(mongoDataSetup, testContext) mongoDataSetups != null -> mongoDataSetups.mongoDataSetup.forEach { document -> insertDocuments(document, testContext) } } }
/** * 테스트가 끝난 이후 모든 모든 데이터를 삭제한다. */ overridefunafterTestMethod(testContext: TestContext) { val mongoTemplate = mongoTemplate(testContext) val currentTestMethod = testContext.testMethod val mongoDataSetup = currentTestMethod.getAnnotation(MongoDataSetup::class.java) val mongoDataSetups = currentTestMethod.getAnnotation(MongoDataSetups::class.java)
MongoDataSetupExecutionListener는 TestExecutionListener를 상속받아 구현되며, 스프링의 테스트 컨텍스트 프레임워크를 사용합니다. 이 리스너는 beforeTestMethod와 afterTestMethod 이벤트를 활용해 테스트 메소드 실행 전에 데이터를 준비하고, 실행 후에 데이터를 정리하는 기능을 수행합니다. 이 리스너는 TestContext에 의존하여 테스트 애플리케이션 컨텍스트에서 Bean을 쉽게 가져올 수 있으며, 예시에서는 MongoTemplate를 추출하는 데 사용되며 테스트 환경을 설정하거나, 테스트 데이터를 초기화하거나, 테스트 결과를 정리하는 등의 작업을 자동화하는 데 유용합니다.TestExecutionListener 인터페이스를 구현하고, 스프링의 테스트에 @TestExecutionListeners 어노테이션을 사용하여 리스너를 등록함으로써 활용할 수 있습니다.
MongoDataSetupExecutionListener는 테스트에 필요한 리스너로, TestExecutionListeners 어노테이션을 통해 등록합니다. 이 과정을 단순화하기 위해 MongoTestSupport 어노테이션을 생성하여, TestExecutionListeners 설정을 손쉽게 적용할 수 있도록 합니다.
dataclassFooProjection( @Field("address_detail") var addressDetail: String, @Field("created_at") val createdAt: LocalDateTime, @Field("updated_at") val updatedAt: LocalDateTime )
@MongoDataSetup( jsonPath = "/mongo-document-foo-projection.json", clazz = FooProjection::class, collectionName = "foo" ) @Test @DisplayName("collectionName 기반으로 테스트") funmongoDataSetupCollectionName() { // when val fooDocuments = mongoTemplate.findAll<Foo>()
@SpringBootTest 어노테이션을 사용하는 테스트 클래스에 @MongoTestSupport를 추가함으로써, 개별 테스트 클래스에서 어노테이션을 중복하여 작성할 필요 없이 모든 테스트에 적용될 수 있으며, 이를 통해 Application Context를 효율적으로 재사용할 수 있습니다. 이러한 방식은 공통적인 설정을 일관되게 관리하는 데에도 도움이 됩니다.
@MongoDataSetup 적절한 사용
@Sql 방식과 마찬가지로, JSON 파일 기반의 데이터 셋업에는 단점이 있습니다. 코드 내에서 객체를 생성하면 변수명과 주석을 통해 명확한 컨텍스트를 제공할 수 있는 반면, JSON 방식은 이러한 세부 사항을 전달하기 어렵습니다. 또한, Document 클래스의 코드 변경 시 관련 JSON 파일을 수동으로 업데이트해야 하는 번거로움이 있습니다.
@MongoDataSetup을 사용한 데이터 셋업은 주로 대량의 데이터나 복잡한 데이터 조합이 필요한 로직을 테스트할 때 추천됩니다. 이런 케이스에서는 코드로 직접 작성할 경우 유지보수 비용이 증가하고, 변수명과 주석을 통한 컨텍스트 전달에 한계가 있기 때문에, 이러한 상황에서 @MongoDataSetup의 사용이 효과적입니다.
마무리
이 포스팅에서는 MongoDB 데이터를 쉽게 설정하는 방법을 소개했지만, 강조하고 싶은 주요 메시지는 테스트 코드의 중요성과 함께 테스트 환경 구성의 중요성입니다.다양한 테스트 케이스를 작성할 수 있는 환경이 준비되어야만, 테스트 코드를 통해 유의미한 피드백을 얻고, 이를 통해 로직을 검증하고 코드 품질을 향상시킬 수 있습니다.