TIL_028 | Kotest, MockMvc, Mockk, describeSpec, SpringBootTest

묘한묘랑·2024년 2월 8일
0

TIL

목록 보기
28/31

강의를 보고 테스트 코드를 짜며 일어난 일이다.


@SpringBootTest
@AutoConfigureMockMvc(addFilters = false)
@ExtendWith(MockKExtension::class)
class PostControllerTest @Autowired constructor(
	private val mockMvc: MockMvc,
	private val jwtPlugin: JwtPlugin
) : DescribeSpec({
	extensions(SpringExtension)

	afterContainer { clearAllMocks() }

	describe("GET /post/{postId}는") {
		val postApiService = mockk<PostApiService>(relaxed = true)
		context("존재하는 ID를 요청을 보낼 때") {
			it("200 status code를 응답해야한다.") {
				val postId = 1L

				every { postApiService.getPostByPostId(any()) } returns PostRes(
					postId = postId,
					title = "Test Title",
					tagList = listOf("태", "그", "리", "스", "트"),
					description = "test descript",
					writer = "test writer",
				)

				val result = mockMvc.perform(
					get("/post/$postId")
						.contentType(MediaType.APPLICATION_JSON)
						.accept(MediaType.APPLICATION_JSON)
				).andReturn()

				result.response.status shouldBe 200

				val responseDto = jacksonObjectMapper().readValue(
					result.response.contentAsString,
					PostRes::class.java
				)

				responseDto.title shouldBe "Test Title"
			}
		}
	}
})

우선 강의를 기반으로 짜게 된다면 이런 형태가 나오는데, 나는 현재 postApiService를 제대로 구현하지 않았다. 어차피 Mocking해서 하는데 구현할 필요또한 느끼지 않았으니까.

그런데 흠... 이상하다.
mockMvc.perform에서 mocking한 service를 사용하지 않는다.
깡통으로 구현해놓은 실제 postApiService.getPostByPostId를 호출하고 있다.

1. 우선 mocking이 제대로 되었나 확인해보자.

val testValue = postApiService.getPostByPostId(postId)
testValue.title shouldBe "Test Title"

위 코드를 넣어 mocking이 제대로 된 것인지 확인을 해보자, mocking은 제대로 되어있었다.

그럼 문제는?

2. mockMvc.perform 에서 mockk로 mocking한 것을 사용하지 않는다.

내가 내린 답은 이것이었다.
우선 이렇게 생각하게 된 이유는 결국 MockMvc와 mockk는 다른 library이기 때문에 그것을 연결하기 위해서는 연결점이 되는 무언가를 setting 해줘야 하는데 처음에는 그 역할을 mockk 함수가 해주는 것으로 판단하였지만 결국 이 방식으로는 mocking한 것을 사용하지 않았기에 이건 아니라고 생각하고 다른 방식으로의 접근이 필요하다고 판단하였다.

3. 일단 검색 또 검색

검색 키워드는 mockk kotest mockMvc 였다.

우선 가장 많이 보이는 형태가 @MockkBean이었다. 우선 이것을 거의 정답이라고 생각은 하였지만, mockk 함수에 대한 의문이 들어 더 검색해보니 mockk 함수와 @mockk의 역할은 mock객체를 만드는 것이지 Spring Application Context에 등록까지는 시키지 않는다는 것이었다.
이게 무엇이 문제냐? Mock객체는 말그대로 객체가 만들어졌을 뿐 Spring Boot Test상에서 Mock객체로 갈아 끼워넣어지는 것은 아니라는 소리다!

자, 그러면 답은 나왔다.

우선 MockkBean을 사용하기 위하여

testImplementation("com.ninja-squad:springmockk:4.0.2")

dependency를 추가하였다.

4. 결과물

@AutoConfigureMockMvc(addFilters = false)
@ExtendWith(MockKExtension::class)
@WebMvcTest(controllers = [PostController::class])
class PostControllerTest @Autowired constructor(
	private val mockMvc: MockMvc
) : DescribeSpec() {
	@MockkBean
	private lateinit var postApiService: PostApiService


	init {
		extensions(SpringExtension)

		afterContainer { clearAllMocks() }

		describe("GET /post/{postId}는") {
			context("존재하는 ID를 요청을 보낼 때") {
				it("200 status code를 응답해야한다.") {
					val postId = 1L

					every { postApiService.getPostByPostId(any()) } returns PostRes(
						postId = postId,
						title = "Test Title",
						tagList = listOf("태", "그", "리", "스", "트"),
						description = "test descript",
						writer = "test writer",
					)

					val testValue = postApiService.getPostByPostId(postId)
					testValue.title shouldBe "Test Title"

					val result = mockMvc.perform(
						get("/post/$postId")
							.contentType(MediaType.APPLICATION_JSON)
							.accept(MediaType.APPLICATION_JSON)
					).andReturn()

				result.response.status shouldBe 200

					val responseDto = jacksonObjectMapper().readValue(
						result.response.contentAsString,
						PostRes::class.java
					)

					responseDto.title shouldBe "Test Title"
				}
			}
		}
	}
}

현재 Security가 적용된 상태이기 때문에 Security를 적용 시키지 않고 사용하기 위하여 addFilters를 false로 주었고, WebMvcTest로 변경하였다.

우선 WebMvcTest로 가게 되면 Controller에 관련된 Bean들만 등록 되는데, Security까지 Test한다고 하면 Filter에 Bean이 필요하기 때문에 테스트 환경에서만 따로 Bean으로 등록 하는 방식을 알아봐야 할 것 같다.


추가 - 2024-02-08 16:53:22

@ComponentScan(basePackageClasses = [JwtPlugin::class])

ComponentScan을 통하여 클래스 기반으로 Bean을 Scan하도록 하여 당장 jwtPlugin이라는 Bean 자체는 개별로 가지고 올 수 있게 되었다.
하지만 Security를 Test하기에는 추가적인 부분이 더 필요할 것 같다.

profile
상황에 맞는 기술을 떠올리고 사용할 수 있는 개발자가 되고 싶은 개발자

0개의 댓글