Aleksandr opened SPR-16550 and commented
When trying to autowire Spring Boot's TestEntityManager
in method annotated as @BeforeAll
, the following exception is thrown:
java.lang.IllegalStateException: No transactional EntityManager found
Other beans are autowired into this method without problems.
The case is following - since I have one test class instance for all tests in it, and they require the same prepopulated DB, why not fill the DB once after test class instantiation?
Sample project is referenced in the issue.
Reference URL: https://gitlab.com/AleksandrSl/spring-sandbox/blob/junit-5-security/src/test/kotlin/pro/parseq/springsandbox/entities/UserItemsSecurityTests.kt
Issue Links: - #10191 Execute all test methods in a class within the same transaction ("duplicates")
Comment From: spring-projects-issues
Juergen Hoeller commented
I'm afraid TestEntityManager
is a Spring Boot specific feature: Please report it to the Spring Boot project at https://github.com/spring-projects/spring-boot/issues instead.
Comment From: spring-projects-issues
Juergen Hoeller commented
With respect to the exception above, this simply means that a persistence operation was triggered without a transaction being active for the current thread. You should be able to inject a TestEntityManager
even in such an per-class callback but every operation called on it would lead to that exception, unless you're manually creating a transaction for those operations.
Comment From: spring-projects-issues
Aleksandr commented
I thought that @Transactional
is responsible for this, because in other test methods and @BeforeEach
method transactions exist
Comment From: spring-projects-issues
Juergen Hoeller commented
Just looked at your test code, I see where you're coming from here now: You expect all such @Before*
to execute within a test-managed transaction, based on the class-level @Transactional
marker.
Sam Brannen, what's your take on this? I suppose BeforeAll
is simply not executing within the TransactionalTestExecutionListener
? It this part of a well-defined lifecycle or potentially worth revisiting?
Comment From: spring-projects-issues
Aleksandr commented
The weird thing, in my point of view, that @BeforeEach
works in transactional context both with method and class level @Transactional
annotations. But
@BeforeAll
is not in context with both annotations, though with Lifecycle.PER_CLASS
@BeforeAll
method is not static and invoked after class instantiation.
Comment From: spring-projects-issues
Sam Brannen commented
The fact that a @BeforeAll
method is not invoked within the test-managed transaction is by design.
The scope of a test-managed transaction is tied to the execution of a test method, test factory, or test template method invocation.
Thus it is not possible for Spring to manage a test-managed transaction at the container level (i.e., when @BeforeAll
and @AfterAll
methods are invoked).
Comment From: spring-projects-issues
Aleksandr commented
So it's not possible to manage transactions in these methods at all, even in the Lifecycle.PER_CLASS @BeforeAll
case?
Comment From: spring-projects-issues
Sam Brannen commented
It this part of a well-defined lifecycle or potentially worth revisiting?
It is part of a well-defined lifecycle; however, similar issues have been raised in the past for TestNG:
-
10191
-
16024
So we could potentially revisit #10191, but... I think the use case described in this issue is actually quite different.
Comment From: spring-projects-issues
Sam Brannen commented
Is your goal to make changes to the database within a test-managed transaction that gets rolled back after the entire class?
Or is your goal to make changes to the database in an isolated, committed transaction before all test methods (which potentially have their own isolated, rolled-back transactions)?
Comment From: spring-projects-issues
Sam Brannen commented
So it's not possible to manage transactions in these methods at all, even in the Lifecycle.PER_CLASS
@BeforeAll
case?
You can of course manage a transaction manually in such a method -- for example, via Spring's TransactionTemplate
; you just cannot have such a method participate in a test-managed transaction that is created for you automatically.
Comment From: spring-projects-issues
Sam Brannen commented
For further details, consult the following sections of the reference manual.
https://docs.spring.io/spring/docs/5.0.4.RELEASE/spring-framework-reference/testing.html#testcontext-tx
https://docs.spring.io/spring/docs/5.0.4.RELEASE/spring-framework-reference/testing.html#testcontext-executing-sql
Comment From: spring-projects-issues
Aleksandr commented
Is your goal to make changes to the database within a test-managed transaction that gets rolled back after the entire class? This is the case. Thanks for the references!
Comment From: spring-projects-issues
Sam Brannen commented
OK.... then my earlier assumption about this being a different topic was wrong.
In light of that and the upcoming support for scenario tests in JUnit Jupiter (potentially in JUnit Jupiter 5.2), I think it would be prudent to reopen #10191.
Consequently, I will close this issue as a duplicate.
Comment From: spring-projects-issues
Sam Brannen commented
FYI: I have reopened #10191.
Comment From: Tockra
Or is your goal to make changes to the database in an isolated, committed transaction before all test methods (which potentially have their own isolated, rolled-back transactions)?
I have the same issues and I'm trying to do exactly what you described above. I want to do a database setup before my tests and then test some stuff. But it don't work with the BeforeAll Method.
My Entity class:
@Entity
@Table(name = "myTable")
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id", scope = CampaignEntity.class)
@EqualsAndHashCode
@ToString
@NoArgsConstructor
@Setter
@Getter
public class MyEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id;
@OneToMany(mappedBy = "someName", cascade = CascadeType.MERGE, orphanRemoval = true)
private final Set<DifferentEntity> additionAttr = new HashSet<>();
}
My before All method:
@BeforeAll
void startUp() {
var myEntities = TestDataGenerator.getListOfEntities(COUNT, ADDITIONAL_ATTR_COUNT);
myRepo.saveAllAndFlush(myEntities); // When I set a debug point here the additionAttr is set with size ADDITIONAL_ATTR_COUNT
}
My test, which fails at the assertion:
@Test
@Transactional
void myFailingTest() throws Exception {
final MyEntity myEntity = this.myRepo.findAll().get(0);
assertThat(myEntity.getAdditionalAttr()).hasSize(ADDITIONAL_ATTR_COUNT); // here it fails because there is size 0 ...
}
Is there a way to fix that issue. it should be possible to do a test setup for the database !?