반응형
통합 테스트 환경을 구축하는 법을 알아보자.
테스트 대상
통합 테스트 환경을 구축하면서 테스트를 해볼 대상은 영속성 (Persistence) 관련 클래스들이다. JPA 를 사용해서 데이터에 대한 생성, 조회, 수정, 삭제, 즉 CRUD 기능을 테스트해보려고 합니다. 테스트를 위한 DB 는 h2 를 사용합니다.
예시에서 사용될 테스트 대상으로 JpaRepository
를 상속받는 BlogStatisticJpaRepository
public interface BlogStatisticJpaRepository extends JpaRepository<BlogStatistic, Long>, BlogStatisticCustomRepository {
}
의존성 설정
아래 의존성을 build.gradle 파일에 적용한다.
// build.gradle.kts
plugins {
id("com.coditory.integration-test") version "1.4.5" apply false
}
dependencies {
integrationImplementation("org.junit.jupiter:junit-jupiter-api")
integrationImplementation("org.junit.jupiter:junit-jupiter-params")
integrationImplementation("org.assertj:assertj-core")
integrationRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
// 테스트 관련 설정을 위한 라이브러리
integrationImplementation("org.springframework.boot:spring-boot-starter-test")
// 테스트 데이터 환경 구축을 위한 h2 DB
integrationRuntimeOnly("com.h2database:h2")
}
여기까지 추가하고 gradle sync
를 한다.
활용한 외부 라이브러리
아래 라이브러리를 활용하면 소스코드 내 "integration" 디렉토리를 IDE 에서 자동으로 통합 테스트를 작성할 수 있는 디렉토리로 인식하게 해준다.
디렉토리 만들기
:libs:adapter-persistence
모듈은 DB 관련 코드가 포함되어 있는 영속성 모듈이다. 영속성 모듈에 통합 테스트를 만들기 위해 src
아래 새로운 디렉토리를 생성한다.
:libs:adapter-persistence 모듈의 src 디렉토리 오른쪽 마우스 클릭 > New > Directory 클릭
이제 통합 테스트를 작성할 수 있는 새로운 디렉토리 integration/java
, integration/resources
를 생성할 수 있다.
설정 (Config.) 파일 만들기
IntegrationConfig.java 와 IntegrationTest.java 파일을 만든다.
- IntegrationConfig.java - 스프링 빈을 사용할 수 있게 @ComponentScan 을 한다.
- IntegrationTest.java - 각 통합 테스트의 base 환경을 제공한다.
IntegrationConfig.java
@Configuration
@ComponentScan(basePackages = "sample.kdohyeon.blog")
public class IntegrationConfig {
}
IntegrationTest.java
- @DataJpaTest 어노테이션 - @Entity 어노테이션이 적용된 클래스를 스캔하며 @Repository 어노테이션이 부여된 클래스들도 포함시킨다.
- @ContextConfiguration - ApplicationContext 관련 속성은 PersistenceJpaConfig 를 참조한다.
@DataJpaTest(includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Repository.class))
@ComponentScan(basePackageClasses = {PersistenceModule.class})
@ContextConfiguration(classes = {PersistenceJpaConfig.class})
public abstract class IntegrationTest {
}
테스트 작성하기
public class BlogStatisticJpaRepositoryTest extends IntegrationTest {
@Autowired
BlogStatisticJpaRepository blogStatisticJpaRepository;
@Test
void save() {
var blogStat = BlogStatistic.create(
CreateBlogStatistic.builder()
.keyword("keyword")
.build()
);
blogStatisticJpaRepository.save(blogStat);
assertThat(blogStat).isNotNull();
assertThat(blogStat.getId()).isNotNull();
assertThat(blogStat.getCount()).isEqualTo(1L);
assertThat(blogStat.getKeyword()).isEqualTo("keyword");
}
}
테스트 결과
- 첫 번째 빨간 박스: @Entity 에 기반해서 테스트 테이블을 만든다
- 두 번째 빨간 박스: 테스트 코드에서 .save(...) 를 하기 때문에 새로운 row 를 insert 한다
- 세 번째 빨간 박스: 테스트 테이블을 제거한다
> Task :buildSrc:compileKotlin UP-TO-DATE
> Task :buildSrc:compileJava NO-SOURCE
> Task :buildSrc:compileGroovy NO-SOURCE
> Task :buildSrc:pluginDescriptors UP-TO-DATE
> Task :buildSrc:processResources NO-SOURCE
> Task :buildSrc:classes UP-TO-DATE
> Task :buildSrc:inspectClassesForKotlinIC UP-TO-DATE
> Task :buildSrc:jar UP-TO-DATE
> Task :buildSrc:assemble UP-TO-DATE
> Task :buildSrc:compileTestKotlin NO-SOURCE
> Task :buildSrc:pluginUnderTestMetadata UP-TO-DATE
> Task :buildSrc:compileTestJava NO-SOURCE
> Task :buildSrc:compileTestGroovy NO-SOURCE
> Task :buildSrc:processTestResources NO-SOURCE
> Task :buildSrc:testClasses UP-TO-DATE
> Task :buildSrc:test NO-SOURCE
> Task :buildSrc:validatePlugins UP-TO-DATE
> Task :buildSrc:check UP-TO-DATE
> Task :buildSrc:build UP-TO-DATE
> Task :libs:adapter-persistence:generateEffectiveLombokConfig
> Task :libs:application:generateEffectiveLombokConfig
> Task :libs:application:compileJava UP-TO-DATE
> Task :libs:adapter-persistence:compileJava UP-TO-DATE
> Task :libs:adapter-persistence:processResources UP-TO-DATE
> Task :libs:adapter-persistence:classes UP-TO-DATE
> Task :libs:adapter-persistence:generateIntegrationEffectiveLombokConfig
> Task :libs:adapter-persistence:generateTestEffectiveLombokConfig
> Task :libs:adapter-persistence:compileTestJava NO-SOURCE
> Task :libs:adapter-persistence:processTestResources NO-SOURCE
> Task :libs:adapter-persistence:testClasses UP-TO-DATE
> Task :libs:adapter-persistence:compileIntegrationJava
> Task :libs:adapter-persistence:processIntegrationResources NO-SOURCE
> Task :libs:adapter-persistence:integrationClasses
> Task :libs:application:processResources NO-SOURCE
> Task :libs:application:classes UP-TO-DATE
> Task :libs:application:jar UP-TO-DATE
> Task :libs:adapter-persistence:integrationTest
09:27:50.376 [Test worker] INFO o.s.t.c.s.AbstractContextLoader - Could not detect default resource locations for test class [sample.kdohyeon.blog.IntegrationTest]: no resource found for suffixes {-context.xml, Context.groovy}.
09:27:50.532 [Test worker] INFO o.s.b.t.a.o.j.DataJpaTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.event.ApplicationEventsTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener]
09:27:50.544 [Test worker] INFO o.s.b.t.a.o.j.DataJpaTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@4b85880b, org.springframework.test.context.event.ApplicationEventsTestExecutionListener@4f936da8, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@4215838f, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@452ba1db, org.springframework.test.context.support.DirtiesContextTestExecutionListener@2289aca5, org.springframework.test.context.transaction.TransactionalTestExecutionListener@76a36b71, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@184497d1, org.springframework.test.context.event.EventPublishingTestExecutionListener@f9d87b, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@6ffab045, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@26fb628, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@3e2943ab, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@70dd7e15, org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener@4a9f80d3, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@35beb15e]
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.7.3)
09:27:51.046 [background-preinit] INFO o.h.validator.internal.util.Version - HV000001: Hibernate Validator 6.2.4.Final
09:27:51.073 [Test worker] INFO s.k.b.p.s.BlogStatisticJpaRepositoryTest - Starting BlogStatisticJpaRepositoryTest using Java 17.0.2 on ip-192-168-0-34.ap-northeast-2.compute.internal with PID 80632 (started by trenbe in /Users/trenbe/IdeaProjects/blog-service/libs/adapter-persistence)
09:27:51.076 [Test worker] INFO s.k.b.p.s.BlogStatisticJpaRepositoryTest - No active profile set, falling back to 1 default profile: "default"
09:27:51.734 [Test worker] INFO o.s.d.r.c.RepositoryConfigurationDelegate - Bootstrapping Spring Data JPA repositories in DEFAULT mode.
09:27:51.811 [Test worker] INFO o.s.d.r.c.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 72 ms. Found 1 JPA repository interfaces.
09:27:51.961 [Test worker] INFO o.s.b.t.a.j.TestDatabaseAutoConfiguration$EmbeddedDataSourceBeanFactoryPostProcessor - Replacing 'dataSource' DataSource bean with embedded version
09:27:52.247 [Test worker] INFO o.s.j.d.e.EmbeddedDatabaseFactory - Starting embedded database: url='jdbc:h2:mem:999a12d6-39f1-4db8-8dea-9a0b37b251b7;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'
09:27:52.695 [Test worker] INFO o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
09:27:52.727 [Test worker] INFO org.hibernate.Version - HHH000412: Hibernate ORM core version 5.6.10.Final
09:27:52.915 [Test worker] INFO o.h.annotations.common.Version - HCANN000001: Hibernate Commons Annotations {5.1.2.Final}
09:27:53.007 [Test worker] INFO org.hibernate.dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
Hibernate: drop table if exists blog_statistic CASCADE
Hibernate: create table blog_statistic (id bigint generated by default as identity, count bigint not null, keyword varchar(255) not null, primary key (id))
09:27:53.454 [Test worker] INFO o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
09:27:53.459 [Test worker] INFO o.s.o.j.LocalContainerEntityManagerFactoryBean - Initialized JPA EntityManagerFactory for persistence unit 'default'
09:27:54.174 [Test worker] INFO s.k.b.p.s.BlogStatisticJpaRepositoryTest - Started BlogStatisticJpaRepositoryTest in 3.591 seconds (JVM running for 5.215)
09:27:54.229 [Test worker] INFO o.s.t.c.t.TransactionContext - Began transaction (1) for test context [DefaultTestContext@3bf26810 testClass = BlogStatisticJpaRepositoryTest, testInstance = sample.kdohyeon.blog.persistence.statistics.BlogStatisticJpaRepositoryTest@19213a74, testMethod = save@BlogStatisticJpaRepositoryTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@30a791a6 testClass = BlogStatisticJpaRepositoryTest, locations = '{}', classes = '{class sample.kdohyeon.blog.configure.PersistenceJpaConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@662f5666, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@502f1f4c, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@5515df6, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@6cbc4f2b, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@618c5d94, [ImportsContextCustomizer@719bb60d key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@33e01298, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@4b3c354a, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@0], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@3beaa16d]; rollback [true]
Hibernate: insert into blog_statistic (id, count, keyword) values (default, ?, ?)
09:27:54.556 [Test worker] INFO o.s.t.c.t.TransactionContext - Rolled back transaction for test: [DefaultTestContext@3bf26810 testClass = BlogStatisticJpaRepositoryTest, testInstance = sample.kdohyeon.blog.persistence.statistics.BlogStatisticJpaRepositoryTest@19213a74, testMethod = save@BlogStatisticJpaRepositoryTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@30a791a6 testClass = BlogStatisticJpaRepositoryTest, locations = '{}', classes = '{class sample.kdohyeon.blog.configure.PersistenceJpaConfig}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@662f5666, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@502f1f4c, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@5515df6, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@6cbc4f2b, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@618c5d94, [ImportsContextCustomizer@719bb60d key = [org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration, org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@33e01298, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@4b3c354a, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@0], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.event.ApplicationEventsTestExecutionListener.recordApplicationEvents' -> false]]
09:27:54.593 [SpringApplicationShutdownHook] INFO o.s.o.j.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'default'
09:27:54.593 [SpringApplicationShutdownHook] INFO o.h.t.s.i.SchemaDropperImpl$DelayedDropActionImpl - HHH000477: Starting delayed evictData of schema as part of SessionFactory shut-down'
Hibernate: drop table if exists blog_statistic CASCADE
BUILD SUCCESSFUL in 10s
16 actionable tasks: 6 executed, 10 up-to-date
9:27:54 AM: Execution finished ':libs:adapter-persistence:integrationTest --tests "sample.kdohyeon.blog.persistence.statistics.BlogStatisticJpaRepositoryTest"'.
참고 자료
반응형
'스프링' 카테고리의 다른 글
[스프링] @Valid 사용과 테스트 케이스 작성하기 (0) | 2023.02.06 |
---|---|
[스프링] REST Docs, asciidoctor 로 API 문서 관리하기 (0) | 2023.02.06 |
[스프링] JUnit5, AssertJ, Mockito 기반 테스트 환경 구축하기 (0) | 2023.02.06 |
[스프링] 스프링에서 관리하는 자바 객체, 빈 (Bean) (0) | 2023.02.03 |
[스프링] 프로그래밍 방식의 트랜잭션 관리 방법 (0) | 2023.02.03 |
댓글