Service 테스트코드 작성
문제
service 테스트 코드 작성 중
존재하지 않는 post 저장했을 때 예외는 잘 통과하는데
존재하는 post 를 저장했을 때 실행하면
NullPointerException 과 함께 실패한다.
코드
package org.example.todolist.domain.post
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.extensions.spring.SpringExtension
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.mockk.clearAllMocks
import io.mockk.every
import io.mockk.junit5.MockKExtension
import io.mockk.mockk
import org.example.todolist.domain.post.model.PostEntity
import org.example.todolist.domain.post.repository.PostRepository
import org.example.todolist.domain.post.service.PostServiceImpl
import org.example.todolist.error.ModelNotFoundException
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.fail
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.data.repository.findByIdOrNull
@SpringBootTest
@ExtendWith(MockKExtension::class)
class PostServiceTest: BehaviorSpec({
extension(SpringExtension)
afterContainer { clearAllMocks() }
val postRepository = mockk<PostRepository>()
val postService = PostServiceImpl(postRepository)
val savedPostId = 10L
val notSavedPostId = 1L
val savedPost = PostEntity(
title = "2",
content = "2",
image = "imageUrl",
author = "1",
memberId = "2",
comments = mutableListOf()
)
every { postRepository.findByIdOrNull(savedPostId) } returns savedPost
Given("post 가 존재할 때 ") {
When("특정 post 를 요청하면") {
Then("PostResponse 를 반환한다.") {
every { postRepository.findByIdOrNull(savedPostId) } returns savedPost
postService.getPost(savedPostId) shouldBe savedPost.toPostResponse()
}
}
}
})
해결과정
mock 객체 사용하여 테스트
=> mock<PostEntity> mock 객체 생성
val savedPost = mockk<PostEntity> {
every { id } returns savedPostId
every { title } returns "2"
every { content } returns "2"
every { image } returns "imageUrl"
every { author } returns "1"
every { memberId } returns "2"
every { comments } returns mutableListOf()
}
=> 마찬가지로 자동생성되는 필드값이 직접 지정해주지 않는 한 null 로 반환된다.
MockKException
mock 객체에서 toPostResponse() 를 호출하면 mockk 가 해당 호출에 대한 반환값을 찾을 수 없다.
mock 은 전부를 mocking 해온다. => 실제 객체의 모든 메서드들을 재정의
stubbing : mock 객체의 특정 메서드 값에 대한 반환 값을 설정하는 것
=> mock 객체의 메서드도 재정의해주어야한다.
toPostResponse() 반환값을 정의해주고 실행해보았다.
Given("mockk test") {
When("특정 post 를 요청하면") {
Then("PostResponse 를 반환한다.") {
val testPost = mockk<PostEntity> {
every { id } returns savedPostId
every { title } returns "2"
every { content } returns "2"
every { image } returns "imageUrl"
every { author } returns "1"
every { memberId } returns "2"
every { comments } returns mutableListOf()
}
every { postRepository.findByIdOrNull(savedPostId) } returns testPost
every { testPost.toPostResponse() } returns PostResponse() // 추가 !
postService.getPost(savedPostId) shouldBe testPost.toPostResponse()
postService.getPost(savedPostId).id shouldBe savedPostId
}
}
}
PostResponse 로 반환은 되지만
id 가 0 으로 담겨있다.
반환값의 id 를 savedId 로 지정해주었다.
Given("mockk test") {
When("특정 post 를 요청하면") {
Then("PostResponse 를 반환한다.") {
val testPost = mockk<PostEntity> {
every { id } returns savedPostId
every { title } returns "2"
every { content } returns "2"
every { image } returns "imageUrl"
every { author } returns "1"
every { memberId } returns "2"
every { comments } returns mutableListOf()
}
every { postRepository.findByIdOrNull(savedPostId) } returns testPost
every { testPost.toPostResponse() } returns PostResponse()
every { testPost.toPostResponse().id } returns savedPostId // 추가 !
postService.getPost(savedPostId) shouldBe testPost.toPostResponse()
postService.getPost(savedPostId).id shouldBe savedPostId
}
}
}
결괏값을 직접 지정해줌 => 테스트 의미 有??
spyk 객체를 사용하여 테스트
spyk는 stubbing 하지 않은 것들에 대해선 mocking 을 하지않는다.
val post = PostEntity(
title = "2",
content = "2",
image = "imageUrl",
author = "1",
memberId = "2",
comments = mutableListOf()
)
val savedPost = spyk<PostEntity>(post)
Given("post 가 존재할 때 ") {
When("특정 post 를 요청하면") {
Then("PostResponse 를 반환한다.") {
every { savedPost.id } returns savedPostId
every { postRepository.findByIdOrNull(savedPostId) } returns savedPost
postService.getPost(savedPostId) shouldBe savedPost.toPostResponse()
postService.getPost(savedPostId).id shouldBe savedPostId
}
}
}
spyk 객체로 postEntity 객체를 생성하고 id 반환값을 savedPostId 값으로 지정했다.
Repository 테스트 코드 작성
문제
@DataJpaTest // jpa 관련 빈 로드
@Transactional
//@Import(JPACongifuration::class)
@Rollback(value = false)
class PostRepositoryTest @Autowired constructor(
private val postRepository: PostRepository
) {
@Test
@DisplayName("게시글 저장 성공")
fun insertTest() {
// Given
val newPost = PostEntity(
title = "게시글1",
content = "게시글1 내용",
image = "imageUrl",
author = "게시글1 테스트",
memberId = "테스트@테스트",
comments = mutableListOf(),
status = PostStatus.FALSE
)
// When
val savedPost = postRepository.save(newPost)
// Then
Assert.notNull(savedPost)
}
}
org.junit.jupiter.api.extension.ParameterResolutionException: Failed to resolve parameter
[org.example.todolist.domain.post.repository.PostRepository postRepository] in constructor
[public org.example.todolist.domain.post.PostRepositoryTest(
org.example.todolist.domain.post.repository.PostRepository)]
@DataJpaTest 대신 @SpringBootTest 를 붙여 다 끌어오니 실행은 된다.
해결과정
참조: https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/autoconfigure/orm/jpa/DataJpaTest.html https://jake-seo-dev.tistory.com/613 https://goateedev.tistory.com/288
내가 원하는건 repository 단독으로 실행하는 것인데 @DataJpaTest 구성을 살펴야 한다.
@DataJpaTest 는 기본적으로 h2 in memory 를 사용한다. 따라서 test h2 설정을 해주어야 한다.
testRuntimeOnly("com.h2database:h2")
다른 db를 사용하기 위해서는 자동 구성 부분의 설정을 바꾸어야 한다.
@DataJpaTest 내부의 @AuthoConfigureTestDatabase replace 를 none 으로 설정 (디폴트 값은 any 라 내장db인 h2 등으로 연결된다.)
@PropertyMapping("spring.test.database") // 설정파일의 placeholder 를 가져옴
public @interface AutoConfigureTestDatabase {
@PropertyMapping(
skip = SkipPropertyMapping.ON_DEFAULT_VALUE
)
// 내장 db를 선택할지말지
Replace replace() default AutoConfigureTestDatabase.Replace.ANY;
// 대체할 내장 db 의 타입
EmbeddedDatabaseConnection connection() default EmbeddedDatabaseConnection.NONE;
TEST 는 누구나 동일한 소스로 test 해볼 수 있어야 하기 때문에 h2 설정하는 것을 추천
테스트 h2 연결 정보
application.yml
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:spring-test
username: sa
password:
'왕초보일지' 카테고리의 다른 글
240215 TIL | (0) | 2024.02.15 |
---|---|
240214 TIL | (0) | 2024.02.14 |
240208 TIL | (0) | 2024.02.08 |
240207 TIL | ObjectMapper (0) | 2024.02.07 |
240206 TIL | (1) | 2024.02.06 |