[만들면서 배우는 클린 아키텍처] 테스트 예제 코드 보충 (#10)

* 만들면서 배우는 클린 아키텍처 initial commit

* refactor : 프로젝트 진입점 클래스 이름 변경

* docs : README.md 헥사고날 아키텍처 항목 추가

* docs(README.md) : 내용 정리 추가

* feat(user.domain) : 도메인 모델 User 추가

* feat(user.domain) : User 의 nickname 프로퍼티 값 객체로 포장

* refactor(User) : 닉네임 변경 함수 이름 수정

* test(user.domain) : 회원 닉네임 변경 테스트 추가

* chore : DB 설정 추가

* feat(user.adapter) : User Entity 구현

* feat : User 닉네임 변경 기능 추가

* refactor(user) : domain 패키지 내부 패키지 구성 추가 및 Entity, Model 이관

* refactor : 사용하지 않는 파일 삭제

* refactor : User 닉네임 변경 기능 컴포넌트 이름 변경

* refactor : User 닉네임 변경 기능 in port 이름 변경

* feat : User Upsert Port 및 Adapter 구현, Service 로직에 추가

* chore : Hexagonal Architecture Process 이미지 추가

* docs(README.md) : 요구사항, 구현 항목 추가

* refactor : 패키지 구성 변경

* feat(user.adapter) : UserMapper 추가 및 적용

* docs(README.md) : 참고자료 및 구현 항목 내용 추가

* refactor : ChangeNicknameRequest, ChangeNicknameResponse 패키지 변경

* refactor : adapter 계층만 application 계층에 의존하도록 통신 객체 추가 및 적용

* refactor : UserEntity @Table 이름 적용

* docs(README.md) : 구현 항목 내용 추가

* refactor : Nickname 입력 유효성 검사 ChangeNicknameRequest 에서 수행하도록 변경

* refactor(user.pojo) : 불필요한 테스트 삭제

* refactor(UserTest) : 오탈자 수정

* build : Kotlin 테스트 라이브러리 추가

* test(user.application) : 닉네임 변경 테스트 추가

* test(user.adapter) : 회원 조회 테스트 추가

* refactor : 불필요한 파일 삭제

* test(user.adapter) : 회원 상태 저장 또는 수정 테스트 추가

* test(user.adapter) : User POJO <-> User Entity 매핑 테스트 추가

* test(user.adapter) : 닉네임 변경 Web Adapter 테스트 추가

* refactor : 불필요한 테스트 파일 삭제

* refactor(user) : 닉네임 변경 테스트 케이스 출력 이름 변경
This commit is contained in:
Colt
2022-04-26 20:57:48 +09:00
committed by GitHub
parent 0f4de6fa3d
commit c5e5a3047b
9 changed files with 247 additions and 25 deletions

View File

@@ -25,11 +25,15 @@ dependencies {
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
runtimeOnly("com.h2database:h2")
runtimeOnly("mysql:mysql-connector-java")
runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
testImplementation("org.springframework.boot:spring-boot-starter-test")
// testImplementation("org.springframework.security:spring-security-test")
testImplementation("io.mockk:mockk:1.12.3")
testImplementation("com.ninja-squad:springmockk:3.1.1")
}
tasks.withType<KotlinCompile> {

View File

@@ -1,11 +0,0 @@
package com.banjjoknim.playground
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class LearnWithMakingCleanArchitectureApplication
fun main(args: Array<String>) {
runApplication<LearnWithMakingCleanArchitectureApplication>(*args)
}

View File

@@ -0,0 +1,72 @@
package com.banjjoknim.cleanarchitecture.user.adapter.`in`.web
import com.banjjoknim.cleanarchitecture.user.application.port.`in`.ChangeNicknameResponseData
import com.banjjoknim.cleanarchitecture.user.application.port.`in`.ChangeNicknameUseCase
import com.fasterxml.jackson.databind.ObjectMapper
import com.ninjasquad.springmockk.MockkBean
import io.mockk.every
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.post
import org.springframework.test.web.servlet.result.MockMvcResultHandlers
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder
import org.springframework.test.web.servlet.setup.MockMvcBuilders
import org.springframework.web.context.WebApplicationContext
import org.springframework.web.filter.CharacterEncodingFilter
@WebMvcTest(ChangeNicknameWebAdapter::class)
class ChangeNicknameWebAdapterTest {
@Autowired
private lateinit var mockmvc: MockMvc
@Autowired
private lateinit var objectMapper: ObjectMapper
@MockkBean
private lateinit var changeNicknameUseCase: ChangeNicknameUseCase
@BeforeEach
fun setUp(webApplicationContext: WebApplicationContext) {
mockmvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.addFilter<DefaultMockMvcBuilder>(CharacterEncodingFilter("UTF-8"))
.alwaysDo<DefaultMockMvcBuilder>(MockMvcResultHandlers.print())
.build()
}
@DisplayName("닉네임 변경 테스트 케이스")
@Nested
inner class ChangeNicknameTestCases {
@Test
fun `닉네임을 변경한다`() {
every { changeNicknameUseCase.changeNickname(any()) } returns ChangeNicknameResponseData(1L)
val request = ChangeNicknameRequest(1L, "banjjoknim")
mockmvc.post("/users") {
contentType = MediaType.APPLICATION_JSON
content = objectMapper.writeValueAsString(request)
}.andExpect {
content { json("""{"userId":1}""") }
status { isOk() }
}
}
@Test
fun `잘못된 닉네임 변경 요청에 BadRequest 응답을 반환한다`() {
val request = ChangeNicknameRequest(1L, "banjjoknim!!")
mockmvc.post("/users") {
contentType = MediaType.APPLICATION_JSON
content = objectMapper.writeValueAsString(request)
}.andExpect {
status { isBadRequest() }
}
}
}
}

View File

@@ -0,0 +1,43 @@
package com.banjjoknim.cleanarchitecture.user.adapter.out.persistence
import com.banjjoknim.cleanarchitecture.user.pojo.Nickname
import org.assertj.core.api.Assertions
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.context.annotation.Import
import org.springframework.test.context.jdbc.Sql
@DataJpaTest
@Import(value = [UserMapper::class, LoadUserPersistenceAdapter::class])
class LoadUserPersistenceAdapterTest {
@Autowired
private lateinit var loadUserPersistenceAdapter: LoadUserPersistenceAdapter
@DisplayName("회원 조회 테스트 케이스")
@Nested
inner class LoadUserTestCases {
@Sql(statements = ["INSERT INTO USER VALUES (1, 'old')"])
@Test
fun `회원이 존재하지 않을 경우 예외가 발생한다`() {
assertThrows<NoSuchElementException>("존재하지 않는 회원입니다. userId: 100") {
loadUserPersistenceAdapter.loadUser(
100L
)
}
}
@Sql(statements = ["INSERT INTO USER VALUES (1, 'old')"])
@Test
fun `회원이 존재할 경우 회원을 조회할 수 있다`() {
val user = loadUserPersistenceAdapter.loadUser(1L)
Assertions.assertThat(user.id).isEqualTo(1)
Assertions.assertThat(user.nickname).isEqualTo(Nickname("old"))
}
}
}

View File

@@ -0,0 +1,39 @@
package com.banjjoknim.cleanarchitecture.user.adapter.out.persistence
import com.banjjoknim.cleanarchitecture.user.pojo.Nickname
import com.banjjoknim.cleanarchitecture.user.pojo.User
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.context.annotation.Import
import org.springframework.data.repository.findByIdOrNull
import org.springframework.test.context.jdbc.Sql
@DataJpaTest
@Import(value = [UserMapper::class, UpsertUserPersistenceAdapter::class])
class UpsertUserPersistenceAdapterTest {
@Autowired
private lateinit var upsertUserPersistenceAdapter: UpsertUserPersistenceAdapter
@Autowired
private lateinit var userEntityRepository: UserEntityRepository
@DisplayName("회원 상태 저장 및 수정 테스트 케이스")
@Nested
inner class UpsertUserTestCases {
@Sql(statements = ["INSERT INTO USER VALUES (1, 'old')"])
@Test
fun `회원 상태를 저장하거나 수정한다`() {
val updatedUser = User(1L, Nickname("new"))
upsertUserPersistenceAdapter.upsertUser(updatedUser)
val userEntity = userEntityRepository.findByIdOrNull(1L)
assertThat(userEntity?.nickname).isEqualTo(NicknameColumn("new"))
}
}
}

View File

@@ -0,0 +1,41 @@
package com.banjjoknim.cleanarchitecture.user.adapter.out.persistence
import com.banjjoknim.cleanarchitecture.user.pojo.Nickname
import com.banjjoknim.cleanarchitecture.user.pojo.User
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest(classes = [UserMapper::class])
class UserMapperTest {
@Autowired
private lateinit var userMapper: UserMapper
@DisplayName("User POJO <-> User Entity 매핑 테스트 케이스")
@Nested
inner class UserMapperTestCases {
@Test
fun `User POJO 를 User Entity 로 변환한다`() {
val user = User(1L, Nickname("banjjoknim"))
val userEntity = userMapper.mapToDomainEntity(user)
assertThat(userEntity.id).isEqualTo(1)
assertThat(userEntity.nickname).isEqualTo(NicknameColumn("banjjoknim"))
}
@Test
fun `User Entity 를 User POJO 로 변환한다`() {
val userEntity = UserEntity(1L, NicknameColumn("banjjoknim"))
val user = userMapper.mapToDomainModel(userEntity)
assertThat(user.id).isEqualTo(1)
assertThat(user.nickname).isEqualTo(Nickname("banjjoknim"))
}
}
}

View File

@@ -0,0 +1,46 @@
package com.banjjoknim.cleanarchitecture.user.application.service
import com.banjjoknim.cleanarchitecture.user.application.port.`in`.ChangeNicknameRequestData
import com.banjjoknim.cleanarchitecture.user.application.port.out.LoadUserPersistencePort
import com.banjjoknim.cleanarchitecture.user.application.port.out.UpsertUserPersistencePort
import com.banjjoknim.cleanarchitecture.user.pojo.Nickname
import com.banjjoknim.cleanarchitecture.user.pojo.User
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
class ChangeNicknameServiceTest {
private val loadUserPersistencePort = mockk<LoadUserPersistencePort>()
private val upsertUserPersistencePort = mockk<UpsertUserPersistencePort>()
private val changeNicknameService = ChangeNicknameService(loadUserPersistencePort, upsertUserPersistencePort)
@DisplayName("닉네임 변경 테스트 케이스")
@Nested
inner class ChangeNicknameTestCases {
private lateinit var testUser: User
private val testChangeNicknameRequestData = ChangeNicknameRequestData(1L, "new")
@BeforeEach
fun setUp() {
testUser = User(1L, Nickname("old"))
}
@Test
fun `닉네임을 변경한다`() {
every { loadUserPersistencePort.loadUser(any()) } returns testUser
every { upsertUserPersistencePort.upsertUser(any()) } just Runs
changeNicknameService.changeNickname(testChangeNicknameRequestData)
assertThat(testUser.nickname).isEqualTo(Nickname("new"))
}
}
}

View File

@@ -7,7 +7,8 @@ import org.junit.jupiter.api.Test
class UserTest {
@DisplayName("회원 이름 변경 테스트 케이스")
@DisplayName("회원 닉네임 변경 테스트 케이스")
@Nested
inner class ChangeNicknameTestCases {
@Test

View File

@@ -1,13 +0,0 @@
package com.banjjoknim.playground
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
@SpringBootTest
class LearnWithMakingCleanArchitectureApplicationTests {
@Test
fun contextLoads() {
}
}