Reputation: 1723
I'm creating a test and basically doing different transactions inside a @Transactional
method.
I add a Project
, then add a Task
to it, and last will fetch the project again from DB to test it has the task saved.
Please note the case I'm showing is a unit test but I'm more interesting in fixing the transactional methods and not the test itself as I already had this in the past in "production code".
Model Classes:
@Entity
@Table(name = "Task")
data class Task(
@Id
@SequenceGenerator(name = "TaskSeq", sequenceName = "TaskSeq", initialValue = 100)
@GeneratedValue(generator = "TaskSeq")
val id: Long = 0,
@Column(nullable = false)
val name: String,
val description: String,
val inZ: LocalDateTime = LocalDateTime.now(),
var outZ: LocalDateTime = JpaConstants.MAX_DATETIME,
var completed: Boolean = false,
@ManyToOne(cascade = [CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH])
@JoinColumn(name = "projectId")
var project: Project? = null
) {
}
@Entity
@Table(name = "Project")
data class Project(
@Id
@SequenceGenerator(name = "ProjectSeq", sequenceName = "ProjectSeq", initialValue = 100)
@GeneratedValue(generator = "ProjectSeq")
val id: Long = 0,
@Column(nullable = false)
var name: String,
@OneToMany(mappedBy = "project", cascade = [CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH])
val tasks: MutableList<Task> = Lists.mutable.empty()
) {
}
Service Classes:
@Service
class ProjectServiceImpl(private val projectRepository: ProjectRepository) : ProjectService {
override fun save(project: Project): Project {
return projectRepository.save(project)
}
}
@Service
class TaskServiceImpl(private val taskRepository: TaskRepository, private val projectRepository: ProjectRepository) : TaskService {
override fun save(task: Task): Task {
return taskRepository.save(task)
}
override fun addTaskToProject(projectId: Long, task: Task): Task {
val project = projectRepository.findById(projectId).orElseThrow { RecordNotFoundException("Couldn't find project with id {$projectId}") }
task.project = project
return save(task)
}
}
The class I'm trying to use the transactional method:
class TaskServiceImplTest : TaskApplicationTests() {
@Autowired
private lateinit var taskService: TaskService
@Autowired
private lateinit var taskRepository: TaskRepository
@Autowired
private lateinit var projectService: ProjectService
@Test
@Transactional
fun canInsertTaskToProject() {
val project = projectService.save(Project(name = "Conquer Paris"))
var task = Task(name = "Check how many people we need to hire", description = "")
task = taskService.addTaskToProject(project.id, task)
assertTrue(task.id > 0)
val projects = projectService.findAll()
assertEquals(1, projects.size())
assertEquals(1, projects[0].tasks.size)
assertEquals(task.id, projects[0].tasks[0].id)
}
If I add a @Transactional(REQUIRES_NEW)
to the methods in the service it will work, but I don't want it as if this method is called inside a real transaction I want it to be rolled back accordingly. Also I'd like to avoid using too many REQUIRES_NEW to avoid future problems
If I remove the @Transactional
from the test method, it won't work when I test the size of the task list on last two lines as they are lazy.
What is the best way to make it work ? I thought that inside a @Transactional
when I used another command from db it would get the latest updates that were not committed yet..
If needed, code in Java is fine too :)
Thanks in advance!
Upvotes: 0
Views: 53
Reputation: 5968
Based on your scenarios, you can use @TestEntityManager
so that each test can be managed in transaction context.
This example can help you,
https://grokonez.com/testing/datajpatest-with-spring-boot
Upvotes: 1