인프런 커뮤니티 질문&답변

w3w님의 프로필 이미지
w3w

작성한 질문수

입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기

[실습] 리포지토리 테스트 코드 작성

projectRepository assertion 오류 질문입니다.

작성

·

57

0

projectRepositoryTest.kt 파일을 아래와 같이 작성했는데 assertion 오류가 나서 원인을 모르겠어서 해당 파일 코드 첨부합니다. 94줄과 111줄 오류인 걸로 보아 skills를 assert할 때 뭐가 잘못된 것 같은데 어떻게 고쳐야 하는지 잘 모르겠습니다..!

package com.yewon.portfolio.domain.repository

import com.yewon.portfolio.domain.constant.SkillType
import com.yewon.portfolio.domain.entity.Project
import com.yewon.portfolio.domain.entity.ProjectDetail
import com.yewon.portfolio.domain.entity.ProjectSkill
import com.yewon.portfolio.domain.entity.Skill
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.*
//import com.yewon.portfolio.domain.entity.*
//import org.assertj.core.api.Assertions.*
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest


@DataJpaTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class ProjectRepositoryTest(
    @Autowired val projectRepository: ProjectRepository,
    @Autowired val skillRepository: SkillRepository
) {
    val DATA_SIZE = 10

    private fun createProject(n: Int): Project {
        val project = Project(
            name = "${n}",
            description = "테스트 설명 {n}",
            startYear = 2023,
            startMonth = 9,
            endYear = 2023,
            endMonth = 9,
            isActive = true
        )

        val details = mutableListOf<ProjectDetail>()
        for (i in 1..n) {
            val projectDetail = ProjectDetail(content = "테스트 ${i}", url = null, isActive = true)
            details.add(projectDetail)
        }
        project.addDetails(details)

        val skills = skillRepository.findAll()
        val skillsUsedInProject = skills.subList(0, n)
        for (skill in skillsUsedInProject) {
            val projectSkill = ProjectSkill(project = project, skill = skill)
            project.skills.add(projectSkill)
        }

        return project
    }

    @BeforeAll
    fun beforeAll() {
        println("----- 스킬 데이터 초기화 시작 -----")
        val skills = mutableListOf<Skill>()
        for (i in 1..DATA_SIZE) {
            val skillTypes = SkillType.values()
            val skill = Skill(name = "테스트 ${i}", type = skillTypes[i%skillTypes.size].name, isActive = true)
            skills.add(skill)
        }
        skillRepository.saveAll(skills)
        println("----- 스킬 데이터 초기화 종료 -----")


//        println("----- 데이터 초기화 이전 조회 시작 -----")
//        val beforeInsert = projectRepository.findAll()
//        assertThat(beforeInsert).hasSize(0)
//        println("----- 데이터 초기화 이전 조회 종료 -----")

        println("----- 테스트 데이터 초기화 시작 -----")
        val projects = mutableListOf<Project>()
        for (i in 1..DATA_SIZE) {
            val project = createProject(i)
            projects.add(project)
        }
        projectRepository.saveAll(projects)
        println("----- 테스트 데이터 초기화 종료 -----")
    }

    @Test
    fun testFindAll() {
        println("----- findAll 테스트 시작 -----")
        val projects = projectRepository.findAll()
        assertThat(projects).hasSize(DATA_SIZE)
        println("projects.size: ${projects.size}")

        for (project in projects) {
            assertThat(project.details).hasSize(project.name.toInt())
            println("project.details.size: ${project.details.size}")

            assertThat(project.skills).hasSize(project.name.toInt())
            println("project.skills.size: ${project.skills.size}")
        }
        println("----- findAll 테스트 종료 -----")
    }

    @Test
    fun testFindAllByIsActive() {
        println("----- findAllByIsActive 테스트 시작 -----")
        val projects = projectRepository.findAllByIsActive(true)
        assertThat(projects).hasSize(DATA_SIZE)
        println("projects.size: ${projects.size}")

        for (project in projects) {
            assertThat(project.details).hasSize(project.name.toInt())
            println("project.details.size: ${project.details.size}")

            assertThat(project.skills).hasSize(project.name.toInt())
            println("project.skills.size: ${project.skills.size}")
        }
        println("----- findAllByIsActive 테스트 종료 -----")
    }
}

답변 1

0

정보근님의 프로필 이미지
정보근
지식공유자

안녕하세요 정보근입니다:)

테스트 코드는 내가 작성한 프로덕션 코드를 '테스트'하는 것이 목적인데요.

위 테스트 코드에서 저희는 리포지토리가 제대로 동작하는지 보기 위해

beforeAll에서 테스트 데이터를 넣고, 테스트 로직을 작성했습니다.

 

그런데 지금 테스트 코드를 보면 초기화나 로직상 문제는 없어보입니다.

즉 테스트 코드 자체가 잘못된 게 아니라면,

테스트 코드를 보기보단 프로덕션 코드를 봐야 하는데요.

 

올려주신 내용만으로는 엔티티 간 cascade 설정이 잘 안 되지 않았을까 하는

추측성 답변 밖에 드릴 수 없겠습니다.

@OneToMany(mappedBy = "project", fetch = FetchType.LAZY, cascade = [CascadeType.PERSIST])
var skills: MutableList<ProjectSkill> = mutableListOf()

Project 엔티티에서 다음과 같이 cascadeType.PERSIST가 지정되었는지 확인해주세요.

 

cascadeType에 대해 추가 설명드리자면,

영속성의 '전이'에 대한 설정인데요.

영속성 컨텍스트에 특정 엔티티가 PERSIST, MERGE, REMOVE, DETACH 등이 될 때

그 엔티티와 연관된 다른 엔티티도 똑같이 상태를 전이할지 말지에 대한 설명입니다.

 

테스트 코드를 보시면, 저희가 project와 skill에 대해서는 save() 메소드를 호출해

엔티티 저장을 명시했습니다.

하지만 projectSkill은 그러지 않고, project.skills에 추가하기만 했습니다.

그런데 Project가 영속선 컨텍스트에 persist, 즉 영속(저장)될 때 ProjectSkill에 전이하지 않는다면

Project만 db에 저장이 되고 ProjectSkill은 저장이 되지 않습니다.

따라서 별도로 ProjectSkillRepository를 이용해 projectSkill을 저장해주시거나,

Project 엔티티의 skills에 cascadeType을 설정해주시면 해결될 것으로 보입니다.

 

감사합니다.

w3w님의 프로필 이미지
w3w
질문자

아....! 네 관련 내용도 덧붙여주셔서 감사합니다.

정보근님의 프로필 이미지
정보근
지식공유자

넵 이 부분은 유사한 질문이 들어와서 강의를 살펴보니,

강의 영상의 코드에서 누락이 되었더라고요.

강의 노트 추가해두었으니 참고 부탁드립니다.

감사합니다!

w3w님의 프로필 이미지
w3w

작성한 질문수

질문하기