From 37f7119284f88745b0215bcbec1af0b7ee06d8ab Mon Sep 17 00:00:00 2001 From: woowahan-pjs Date: Wed, 28 Sep 2022 09:15:48 +0900 Subject: [PATCH 1/6] feat(apply): update the version to fix the h2 bug and respond to the new version --- build.gradle.kts | 7 ++++--- src/main/kotlin/apply/config/Databases.kt | 3 ++- src/main/kotlin/apply/domain/user/User.kt | 2 ++ src/main/resources/application-test.properties | 2 +- .../db/migration/V3_3__Change_user_table_name.sql | 1 + src/test/kotlin/support/test/BaseTests.kt | 2 ++ 6 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 src/main/resources/db/migration/V3_3__Change_user_table_name.sql diff --git a/build.gradle.kts b/build.gradle.kts index c53194840..14f5223f7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -51,14 +51,15 @@ dependencies { runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.2") runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.2") runtimeOnly("mysql:mysql-connector-java") - runtimeOnly("com.h2database:h2") + runtimeOnly("com.h2database:h2:2.1.214") testImplementation("org.springframework.boot:spring-boot-starter-test") { exclude(group = "org.junit.vintage", module = "junit-vintage-engine") exclude(group = "org.mockito") } - testImplementation("com.ninja-squad:springmockk:2.0.3") - asciidoctorExt("org.springframework.restdocs:spring-restdocs-asciidoctor") + testImplementation("org.springframework.boot:spring-boot-starter-webflux") testImplementation("org.springframework.restdocs:spring-restdocs-mockmvc") + asciidoctorExt("org.springframework.restdocs:spring-restdocs-asciidoctor") + testImplementation("com.ninja-squad:springmockk:2.0.3") testImplementation("io.kotest:kotest-runner-junit5:5.4.2") testImplementation("io.kotest.extensions:kotest-extensions-spring:1.1.2") } diff --git a/src/main/kotlin/apply/config/Databases.kt b/src/main/kotlin/apply/config/Databases.kt index 15557abc3..453afe69d 100644 --- a/src/main/kotlin/apply/config/Databases.kt +++ b/src/main/kotlin/apply/config/Databases.kt @@ -63,7 +63,8 @@ class H2( entityManager: EntityManager, properties: DatabaseInitializationProperties ) : AbstractDatabase(entityManager, properties) { - override val metaTablesSql: String = "show tables" + override val metaTablesSql: String = + "select table_name from information_schema.tables where table_schema = 'PUBLIC'" override val constraintsOffSql: String = "set referential_integrity false" override val constraintsOnSql: String = "set referential_integrity true" override fun createTruncateTableSql(tableName: String): String = "truncate table $tableName restart identity" diff --git a/src/main/kotlin/apply/domain/user/User.kt b/src/main/kotlin/apply/domain/user/User.kt index 51bb267d8..2cc1e0547 100644 --- a/src/main/kotlin/apply/domain/user/User.kt +++ b/src/main/kotlin/apply/domain/user/User.kt @@ -6,7 +6,9 @@ import javax.persistence.AttributeOverride import javax.persistence.Column import javax.persistence.Embedded import javax.persistence.Entity +import javax.persistence.Table +@Table(name = "users") @Entity class User( @Embedded diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties index d387c3edf..af42dac8b 100644 --- a/src/main/resources/application-test.properties +++ b/src/main/resources/application-test.properties @@ -1,4 +1,4 @@ -spring.datasource.url=jdbc:h2:~/test;MODE=MYSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE +spring.datasource.url=jdbc:h2:~/apply;MODE=MYSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.properties.hibernate.format_sql=true diff --git a/src/main/resources/db/migration/V3_3__Change_user_table_name.sql b/src/main/resources/db/migration/V3_3__Change_user_table_name.sql new file mode 100644 index 000000000..746abeb7b --- /dev/null +++ b/src/main/resources/db/migration/V3_3__Change_user_table_name.sql @@ -0,0 +1 @@ +alter table user rename users; diff --git a/src/test/kotlin/support/test/BaseTests.kt b/src/test/kotlin/support/test/BaseTests.kt index 90bd76b91..b28b5314e 100644 --- a/src/test/kotlin/support/test/BaseTests.kt +++ b/src/test/kotlin/support/test/BaseTests.kt @@ -2,6 +2,7 @@ package support.test import io.mockk.junit5.MockKExtension import org.junit.jupiter.api.extension.ExtendWith +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.context.ActiveProfiles @@ -20,6 +21,7 @@ annotation class UnitTest @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.RUNTIME) +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @DataJpaTest @TestEnvironment annotation class RepositoryTest From 8ffa78fde3c09c73ea684e4eb15ee76dc68355d4 Mon Sep 17 00:00:00 2001 From: woowahan-pjs Date: Wed, 28 Sep 2022 09:20:49 +0900 Subject: [PATCH 2/6] test(apply): add an example of generating a term using the acceptance dsl --- .../apply/acceptance/AcceptanceDslTest.kt | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/test/kotlin/apply/acceptance/AcceptanceDslTest.kt diff --git a/src/test/kotlin/apply/acceptance/AcceptanceDslTest.kt b/src/test/kotlin/apply/acceptance/AcceptanceDslTest.kt new file mode 100644 index 000000000..cf6492ed0 --- /dev/null +++ b/src/test/kotlin/apply/acceptance/AcceptanceDslTest.kt @@ -0,0 +1,61 @@ +package apply.acceptance + +import apply.config.Database +import apply.createUser +import apply.security.LoginUserResolver +import com.ninjasquad.springmockk.SpykBean +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.mockk.every +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.http.HttpHeaders.LOCATION +import org.springframework.http.MediaType.APPLICATION_JSON +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.web.reactive.server.WebTestClient + +@SpykBean(LoginUserResolver::class) +@ActiveProfiles("test") +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +class AcceptanceDslTest( + private val client: WebTestClient, + private val database: Database, + private val loginUserResolver: LoginUserResolver +) : StringSpec({ + every { loginUserResolver.resolveArgument(any(), any(), any(), any()) } returns createUser() + + "기수 생성 예시" { + val term1 = with(client) { + term() + } + val term2 = with(client) { + term { + name = "5기" + } + } + term1.id shouldNotBe term2.id + term2.name shouldBe "5기" + } + + afterTest { + database.clear(database.retrieveTables()) + } +}) + +data class Term( + var name: String = "4기", + var id: Long = 0L +) + +fun WebTestClient.term(block: Term.() -> Unit = {}): Term { + val term = Term().apply(block) + post().uri("/api/terms") + .contentType(APPLICATION_JSON) + .bodyValue(term) + .exchange() + .expectStatus().isCreated + .expectHeader().value(LOCATION) { term.id = it.extractId() } + return term +} + +private fun String.extractId(): Long = substringAfterLast("/").toLong() From 971ae5ca71d54969274dcd17afa82750ab8a5cd8 Mon Sep 17 00:00:00 2001 From: woowahan-pjs Date: Wed, 28 Sep 2022 09:32:25 +0900 Subject: [PATCH 3/6] feat(properties): exclude the vaadin configuration for test --- src/main/resources/application-test.properties | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties index af42dac8b..41d7f5aa4 100644 --- a/src/main/resources/application-test.properties +++ b/src/main/resources/application-test.properties @@ -1,3 +1,5 @@ +spring.autoconfigure.exclude=com.vaadin.flow.spring.SpringBootAutoConfiguration + spring.datasource.url=jdbc:h2:~/apply;MODE=MYSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE spring.jpa.hibernate.ddl-auto=create-drop From 6ceb8a1e14bc05e301b6765f545a5f8374630bd7 Mon Sep 17 00:00:00 2001 From: woowahan-pjs Date: Wed, 28 Sep 2022 13:08:40 +0900 Subject: [PATCH 4/6] test(apply): add an example of generating a recruitment using the acceptance dsl --- .../apply/acceptance/AcceptanceDslTest.kt | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/test/kotlin/apply/acceptance/AcceptanceDslTest.kt b/src/test/kotlin/apply/acceptance/AcceptanceDslTest.kt index cf6492ed0..fd8f453d3 100644 --- a/src/test/kotlin/apply/acceptance/AcceptanceDslTest.kt +++ b/src/test/kotlin/apply/acceptance/AcceptanceDslTest.kt @@ -5,6 +5,7 @@ import apply.createUser import apply.security.LoginUserResolver import com.ninjasquad.springmockk.SpykBean import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.longs.shouldNotBeZero import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import io.mockk.every @@ -13,6 +14,9 @@ import org.springframework.http.HttpHeaders.LOCATION import org.springframework.http.MediaType.APPLICATION_JSON import org.springframework.test.context.ActiveProfiles import org.springframework.test.web.reactive.server.WebTestClient +import support.createLocalDateTime +import java.time.LocalDateTime +import java.time.LocalDateTime.now @SpykBean(LoginUserResolver::class) @ActiveProfiles("test") @@ -37,11 +41,68 @@ class AcceptanceDslTest( term2.name shouldBe "5기" } + "모집 생성 예시" { + val recruitment1 = with(client) { + recruitment() + } + val recruitment2 = with(client) { + recruitment { + title = "웹 백엔드 5기" + term = term { + name = "5기" + } + startDateTime = createLocalDateTime(2022, 10, 17) + endDateTime = createLocalDateTime(2022, 10, 24) + } + } + val term = with(client) { + term { + name = "6기" + } + } + val recruitment3 = with(client) { + recruitment { + title = "웹 백엔드 6기" + termId = term.id + } + } + recruitment1.id.shouldNotBeZero() + recruitment2.title shouldBe "웹 백엔드 5기" + recruitment3.title shouldBe "웹 백엔드 6기" + } + afterTest { database.clear(database.retrieveTables()) } }) +private val now: LocalDateTime = now() + +data class Recruitment( + var title: String = "웹 백엔드 4기", + var term: Term? = null, + var termId: Long? = null, + var startDateTime: LocalDateTime = now.minusYears(1), + var endDateTime: LocalDateTime = now.plusYears(1), + var recruitable: Boolean = false, + var hidden: Boolean = true, + var id: Long = 0L +) + +fun WebTestClient.recruitment(block: Recruitment.() -> Unit = {}): Recruitment { + val recruitment = Recruitment().apply(block) + if (recruitment.term == null) { + recruitment.term = Term(id = recruitment.termId ?: term().id) + } + post().uri("/api/recruitments") + .contentType(APPLICATION_JSON) + .bodyValue(recruitment) + .exchange() + .expectStatus().isCreated + .expectHeader().value(LOCATION) { recruitment.id = it.extractId() } + return recruitment +} + data class Term( var name: String = "4기", var id: Long = 0L From 82d97bbde342d95033cac3154d7460c609569375 Mon Sep 17 00:00:00 2001 From: woowahan-pjs Date: Wed, 28 Sep 2022 14:14:19 +0900 Subject: [PATCH 5/6] test(apply): add recruitment item acceptance dsl --- .../apply/acceptance/AcceptanceDslTest.kt | 57 +++++++++++++------ 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/src/test/kotlin/apply/acceptance/AcceptanceDslTest.kt b/src/test/kotlin/apply/acceptance/AcceptanceDslTest.kt index fd8f453d3..d97217435 100644 --- a/src/test/kotlin/apply/acceptance/AcceptanceDslTest.kt +++ b/src/test/kotlin/apply/acceptance/AcceptanceDslTest.kt @@ -5,7 +5,7 @@ import apply.createUser import apply.security.LoginUserResolver import com.ninjasquad.springmockk.SpykBean import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.longs.shouldNotBeZero +import io.kotest.matchers.collections.shouldNotContainDuplicates import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import io.mockk.every @@ -46,6 +46,11 @@ class AcceptanceDslTest( recruitment() } val recruitment2 = with(client) { + recruitment { + termId = 0L + } + } + val recruitment3 = with(client) { recruitment { title = "웹 백엔드 5기" term = term { @@ -53,22 +58,19 @@ class AcceptanceDslTest( } startDateTime = createLocalDateTime(2022, 10, 17) endDateTime = createLocalDateTime(2022, 10, 24) + recruitmentItems = recruitmentItems { + recruitmentItem { + title = "프로그래밍 학습 과정과 현재 자신이 생각하는 역량은?" + position = 1 + } + recruitmentItem { + title = "프로그래머가 되려는 이유는 무엇인가요?" + position = 2 + } + } } } - val term = with(client) { - term { - name = "6기" - } - } - val recruitment3 = with(client) { - recruitment { - title = "웹 백엔드 6기" - termId = term.id - } - } - recruitment1.id.shouldNotBeZero() - recruitment2.title shouldBe "웹 백엔드 5기" - recruitment3.title shouldBe "웹 백엔드 6기" + listOf(recruitment1.id, recruitment2.id, recruitment3.id).shouldNotContainDuplicates() } afterTest { @@ -81,18 +83,39 @@ private val now: LocalDateTime = now() data class Recruitment( var title: String = "웹 백엔드 4기", var term: Term? = null, - var termId: Long? = null, + var termId: Long = 0L, var startDateTime: LocalDateTime = now.minusYears(1), var endDateTime: LocalDateTime = now.plusYears(1), var recruitable: Boolean = false, var hidden: Boolean = true, + var recruitmentItems: List = emptyList(), var id: Long = 0L ) +data class RecruitmentItem( + var title: String = "프로그래밍 학습 과정과 현재 자신이 생각하는 역량은?", + var position: Int = 1, + var maximumLength: Int = 1000, + var description: String = "현재 어느 정도의 역량을 보유한 상태인지를 구체적으로 작성해 주세요.", + var id: Long = 0L +) + +class RecruitmentItems { + val items: MutableList = mutableListOf() + + fun recruitmentItem(block: RecruitmentItem.() -> Unit = {}) { + items.add(RecruitmentItem().apply(block)) + } +} + +fun recruitmentItems(block: RecruitmentItems.() -> Unit): List { + return RecruitmentItems().apply(block).items +} + fun WebTestClient.recruitment(block: Recruitment.() -> Unit = {}): Recruitment { val recruitment = Recruitment().apply(block) if (recruitment.term == null) { - recruitment.term = Term(id = recruitment.termId ?: term().id) + recruitment.term = Term(id = recruitment.termId) } post().uri("/api/recruitments") .contentType(APPLICATION_JSON) From a3be2170299f27b4989e7cf6bafdf2c98a9b5534 Mon Sep 17 00:00:00 2001 From: woowahan-pjs Date: Thu, 29 Sep 2022 01:10:12 +0900 Subject: [PATCH 6/6] refactor(test): separate the abstract acceptance test --- .../kotlin/apply/acceptance/AcceptanceDsl.kt | 72 ++++++++ .../apply/acceptance/AcceptanceDslTest.kt | 162 ++++-------------- .../kotlin/apply/acceptance/AcceptanceTest.kt | 50 ++++++ 3 files changed, 158 insertions(+), 126 deletions(-) create mode 100644 src/test/kotlin/apply/acceptance/AcceptanceDsl.kt create mode 100644 src/test/kotlin/apply/acceptance/AcceptanceTest.kt diff --git a/src/test/kotlin/apply/acceptance/AcceptanceDsl.kt b/src/test/kotlin/apply/acceptance/AcceptanceDsl.kt new file mode 100644 index 000000000..6fa2d8207 --- /dev/null +++ b/src/test/kotlin/apply/acceptance/AcceptanceDsl.kt @@ -0,0 +1,72 @@ +package apply.acceptance + +import org.springframework.http.HttpHeaders +import org.springframework.http.MediaType +import org.springframework.test.web.reactive.server.WebTestClient +import java.time.LocalDateTime + +private val now: LocalDateTime = LocalDateTime.now() + +data class Recruitment( + var title: String = "웹 백엔드 4기", + var term: Term? = null, + var termId: Long = 0L, + var startDateTime: LocalDateTime = now.minusYears(1), + var endDateTime: LocalDateTime = now.plusYears(1), + var recruitable: Boolean = false, + var hidden: Boolean = true, + var recruitmentItems: List = emptyList(), + var id: Long = 0L +) + +data class RecruitmentItem( + var title: String = "프로그래밍 학습 과정과 현재 자신이 생각하는 역량은?", + var position: Int = 1, + var maximumLength: Int = 1000, + var description: String = "현재 어느 정도의 역량을 보유한 상태인지를 구체적으로 작성해 주세요.", + var id: Long = 0L +) + +class RecruitmentItems { + val items: MutableList = mutableListOf() + + fun recruitmentItem(block: RecruitmentItem.() -> Unit = {}) { + items.add(RecruitmentItem().apply(block)) + } +} + +fun recruitmentItems(block: RecruitmentItems.() -> Unit): List { + return RecruitmentItems().apply(block).items +} + +fun WebTestClient.recruitment(block: Recruitment.() -> Unit = {}): Recruitment { + val recruitment = Recruitment().apply(block) + if (recruitment.term == null) { + recruitment.term = Term(id = recruitment.termId) + } + post().uri("/api/recruitments") + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(recruitment) + .exchange() + .expectStatus().isCreated + .expectHeader().value(HttpHeaders.LOCATION) { recruitment.id = it.extractId() } + return recruitment +} + +data class Term( + var name: String = "4기", + var id: Long = 0L +) + +fun WebTestClient.term(block: Term.() -> Unit = {}): Term { + val term = Term().apply(block) + post().uri("/api/terms") + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(term) + .exchange() + .expectStatus().isCreated + .expectHeader().value(HttpHeaders.LOCATION) { term.id = it.extractId() } + return term +} + +private fun String.extractId(): Long = substringAfterLast("/").toLong() diff --git a/src/test/kotlin/apply/acceptance/AcceptanceDslTest.kt b/src/test/kotlin/apply/acceptance/AcceptanceDslTest.kt index d97217435..73df1ec9e 100644 --- a/src/test/kotlin/apply/acceptance/AcceptanceDslTest.kt +++ b/src/test/kotlin/apply/acceptance/AcceptanceDslTest.kt @@ -1,145 +1,55 @@ package apply.acceptance -import apply.config.Database -import apply.createUser -import apply.security.LoginUserResolver -import com.ninjasquad.springmockk.SpykBean -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldNotContainDuplicates import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe -import io.mockk.every -import org.springframework.boot.test.context.SpringBootTest -import org.springframework.http.HttpHeaders.LOCATION -import org.springframework.http.MediaType.APPLICATION_JSON -import org.springframework.test.context.ActiveProfiles -import org.springframework.test.web.reactive.server.WebTestClient import support.createLocalDateTime -import java.time.LocalDateTime -import java.time.LocalDateTime.now -@SpykBean(LoginUserResolver::class) -@ActiveProfiles("test") -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -class AcceptanceDslTest( - private val client: WebTestClient, - private val database: Database, - private val loginUserResolver: LoginUserResolver -) : StringSpec({ - every { loginUserResolver.resolveArgument(any(), any(), any(), any()) } returns createUser() +class AcceptanceDslTest : AcceptanceTest() { + init { + Given("기수 생성 예시") { + When("기수를 생성하면") { + val term1 = term() + val term2 = term { + name = "5기" + } - "기수 생성 예시" { - val term1 = with(client) { - term() - } - val term2 = with(client) { - term { - name = "5기" + Then("기수가 생성된다") { + term1.id shouldNotBe term2.id + term2.name shouldBe "5기" + } } } - term1.id shouldNotBe term2.id - term2.name shouldBe "5기" - } - "모집 생성 예시" { - val recruitment1 = with(client) { - recruitment() - } - val recruitment2 = with(client) { - recruitment { - termId = 0L - } - } - val recruitment3 = with(client) { - recruitment { - title = "웹 백엔드 5기" - term = term { - name = "5기" + Given("모집 생성 예시") { + When("모집을 생성하면") { + val recruitment1 = recruitment() + val recruitment2 = recruitment { + termId = 0L } - startDateTime = createLocalDateTime(2022, 10, 17) - endDateTime = createLocalDateTime(2022, 10, 24) - recruitmentItems = recruitmentItems { - recruitmentItem { - title = "프로그래밍 학습 과정과 현재 자신이 생각하는 역량은?" - position = 1 + val recruitment3 = recruitment { + title = "웹 백엔드 5기" + term = term { + name = "5기" } - recruitmentItem { - title = "프로그래머가 되려는 이유는 무엇인가요?" - position = 2 + startDateTime = createLocalDateTime(2022, 10, 17) + endDateTime = createLocalDateTime(2022, 10, 24) + recruitmentItems = recruitmentItems { + recruitmentItem { + title = "프로그래밍 학습 과정과 현재 자신이 생각하는 역량은?" + position = 1 + } + recruitmentItem { + title = "프로그래머가 되려는 이유는 무엇인가요?" + position = 2 + } } } + + Then("모집이 생성된다") { + listOf(recruitment1.id, recruitment2.id, recruitment3.id).shouldNotContainDuplicates() + } } } - listOf(recruitment1.id, recruitment2.id, recruitment3.id).shouldNotContainDuplicates() - } - - afterTest { - database.clear(database.retrieveTables()) - } -}) - -private val now: LocalDateTime = now() - -data class Recruitment( - var title: String = "웹 백엔드 4기", - var term: Term? = null, - var termId: Long = 0L, - var startDateTime: LocalDateTime = now.minusYears(1), - var endDateTime: LocalDateTime = now.plusYears(1), - var recruitable: Boolean = false, - var hidden: Boolean = true, - var recruitmentItems: List = emptyList(), - var id: Long = 0L -) - -data class RecruitmentItem( - var title: String = "프로그래밍 학습 과정과 현재 자신이 생각하는 역량은?", - var position: Int = 1, - var maximumLength: Int = 1000, - var description: String = "현재 어느 정도의 역량을 보유한 상태인지를 구체적으로 작성해 주세요.", - var id: Long = 0L -) - -class RecruitmentItems { - val items: MutableList = mutableListOf() - - fun recruitmentItem(block: RecruitmentItem.() -> Unit = {}) { - items.add(RecruitmentItem().apply(block)) - } -} - -fun recruitmentItems(block: RecruitmentItems.() -> Unit): List { - return RecruitmentItems().apply(block).items -} - -fun WebTestClient.recruitment(block: Recruitment.() -> Unit = {}): Recruitment { - val recruitment = Recruitment().apply(block) - if (recruitment.term == null) { - recruitment.term = Term(id = recruitment.termId) } - post().uri("/api/recruitments") - .contentType(APPLICATION_JSON) - .bodyValue(recruitment) - .exchange() - .expectStatus().isCreated - .expectHeader().value(LOCATION) { recruitment.id = it.extractId() } - return recruitment } - -data class Term( - var name: String = "4기", - var id: Long = 0L -) - -fun WebTestClient.term(block: Term.() -> Unit = {}): Term { - val term = Term().apply(block) - post().uri("/api/terms") - .contentType(APPLICATION_JSON) - .bodyValue(term) - .exchange() - .expectStatus().isCreated - .expectHeader().value(LOCATION) { term.id = it.extractId() } - return term -} - -private fun String.extractId(): Long = substringAfterLast("/").toLong() diff --git a/src/test/kotlin/apply/acceptance/AcceptanceTest.kt b/src/test/kotlin/apply/acceptance/AcceptanceTest.kt new file mode 100644 index 000000000..b0d24303e --- /dev/null +++ b/src/test/kotlin/apply/acceptance/AcceptanceTest.kt @@ -0,0 +1,50 @@ +package apply.acceptance + +import apply.config.Database +import apply.createUser +import apply.security.LoginUserResolver +import com.ninjasquad.springmockk.SpykBean +import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.extensions.spring.SpringTestExtension +import io.kotest.extensions.spring.SpringTestLifecycleMode +import io.mockk.every +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.test.web.reactive.server.WebTestClient +import support.test.TestEnvironment +import support.test.spec.afterRootTest +import support.test.spec.beforeRootTest + +@SpykBean(LoginUserResolver::class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@TestEnvironment +abstract class AcceptanceTest : BehaviorSpec() { + @Autowired + lateinit var client: WebTestClient + + @Autowired + private lateinit var database: Database + + @Autowired + private lateinit var loginUserResolver: LoginUserResolver + + init { + extensions(SpringTestExtension(SpringTestLifecycleMode.Root)) + + beforeRootTest { + every { loginUserResolver.resolveArgument(any(), any(), any(), any()) } returns createUser() + } + + afterRootTest { + database.clear(database.retrieveTables()) + } + } + + fun recruitment(block: Recruitment.() -> Unit = {}): Recruitment { + return client.recruitment(block) + } + + fun term(block: Term.() -> Unit = {}): Term { + return client.term(block) + } +}