diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideTestExecutionListener.java index 7237b8cfbaf..229fdb21553 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideTestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideTestExecutionListener.java @@ -95,7 +95,7 @@ public class BeanOverrideTestExecutionListener extends AbstractTestExecutionList private static void injectFields(TestContext testContext) { Object testInstance = testContext.getTestInstance(); // Since JUnit Jupiter 5.12, if the SpringExtension is used with Jupiter's - // ExtensionContextScope.TEST_METHOD mode, the value returned from + // TEST_METHOD ExtensionContextScope, the value returned from // testContext.getTestClass() may refer to the declaring class of the test // method which is about to be invoked (which may be in a @Nested class // within the class for the test instance). Thus, we use the class for the @@ -109,7 +109,7 @@ public class BeanOverrideTestExecutionListener extends AbstractTestExecutionList Assert.state(applicationContext.containsBean(BeanOverrideRegistry.BEAN_NAME), () -> """ Test class %s declares @BeanOverride fields %s, but no BeanOverrideHandler has been registered. \ If you are using @ContextHierarchy, ensure that context names for bean overrides match \ - configured @ContextConfiguration names.""".formatted(testContext.getTestClass().getSimpleName(), + configured @ContextConfiguration names.""".formatted(testClass.getSimpleName(), handlers.stream().map(BeanOverrideHandler::getField).filter(Objects::nonNull) .map(Field::getName).toList())); BeanOverrideRegistry beanOverrideRegistry = applicationContext.getBean(BeanOverrideRegistry.BEAN_NAME, diff --git a/spring-test/src/test/java/org/springframework/test/context/orm/jpa/JpaConfig.java b/spring-test/src/test/java/org/springframework/test/context/orm/jpa/JpaConfig.java new file mode 100644 index 00000000000..ca98ba2e3e4 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/orm/jpa/JpaConfig.java @@ -0,0 +1,72 @@ +/* + * Copyright 2002-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.orm.jpa; + +import javax.sql.DataSource; + +import jakarta.persistence.EntityManagerFactory; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.Database; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.test.context.orm.jpa.domain.JpaPersonRepository; +import org.springframework.test.context.orm.jpa.domain.Person; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +@Configuration(proxyBeanMethods = false) +@EnableTransactionManagement +class JpaConfig { + + @Bean + JpaPersonRepository personRepository() { + return new JpaPersonRepository(); + } + + @Bean + EmbeddedDatabase dataSource() { + return new EmbeddedDatabaseBuilder().generateUniqueName(true).build(); + } + + @Bean + JdbcTemplate jdbcTemplate(DataSource dataSource) { + return new JdbcTemplate(dataSource); + } + + @Bean + LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) { + LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean(); + emfb.setDataSource(dataSource); + emfb.setPackagesToScan(Person.class.getPackage().getName()); + HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter(); + hibernateJpaVendorAdapter.setGenerateDdl(true); + hibernateJpaVendorAdapter.setDatabase(Database.H2); + emfb.setJpaVendorAdapter(hibernateJpaVendorAdapter); + return emfb; + } + + @Bean + JpaTransactionManager transactionManager(EntityManagerFactory emf) { + return new JpaTransactionManager(emf); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/orm/jpa/JpaEntityListenerTests.java b/spring-test/src/test/java/org/springframework/test/context/orm/jpa/JpaEntityListenerTests.java index d9b9bb8768d..5f1267c15d1 100644 --- a/spring-test/src/test/java/org/springframework/test/context/orm/jpa/JpaEntityListenerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/orm/jpa/JpaEntityListenerTests.java @@ -18,30 +18,18 @@ package org.springframework.test.context.orm.jpa; import java.util.List; -import javax.sql.DataSource; - import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.PersistenceContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; -import org.springframework.orm.jpa.JpaTransactionManager; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; -import org.springframework.orm.jpa.vendor.Database; -import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; -import org.springframework.test.context.orm.jpa.domain.JpaPersonRepository; import org.springframework.test.context.orm.jpa.domain.Person; import org.springframework.test.context.orm.jpa.domain.PersonListener; import org.springframework.test.context.orm.jpa.domain.PersonRepository; -import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.Transactional; import static org.assertj.core.api.Assertions.assertThat; @@ -55,7 +43,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @see issue gh-28228 * @see org.springframework.test.context.orm.hibernate.HibernateSessionFlushingTests */ -@SpringJUnitConfig +@SpringJUnitConfig(JpaConfig.class) @Transactional @Sql(statements = "insert into person(id, name) values(0, 'Jane')") class JpaEntityListenerTests { @@ -156,43 +144,4 @@ class JpaEntityListenerTests { } } - - @Configuration(proxyBeanMethods = false) - @EnableTransactionManagement - static class Config { - - @Bean - PersonRepository personRepository() { - return new JpaPersonRepository(); - } - - @Bean - DataSource dataSource() { - return new EmbeddedDatabaseBuilder().generateUniqueName(true).build(); - } - - @Bean - JdbcTemplate jdbcTemplate(DataSource dataSource) { - return new JdbcTemplate(dataSource); - } - - @Bean - LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) { - LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean(); - emfb.setDataSource(dataSource); - emfb.setPackagesToScan(Person.class.getPackage().getName()); - HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter(); - hibernateJpaVendorAdapter.setGenerateDdl(true); - hibernateJpaVendorAdapter.setDatabase(Database.HSQL); - emfb.setJpaVendorAdapter(hibernateJpaVendorAdapter); - return emfb; - } - - @Bean - JpaTransactionManager transactionManager(EntityManagerFactory emf) { - return new JpaTransactionManager(emf); - } - - } - } diff --git a/spring-test/src/test/java/org/springframework/test/context/orm/jpa/JpaPersonRepositoryTests.java b/spring-test/src/test/java/org/springframework/test/context/orm/jpa/JpaPersonRepositoryTests.java new file mode 100644 index 00000000000..9811a848d55 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/orm/jpa/JpaPersonRepositoryTests.java @@ -0,0 +1,98 @@ +/* + * Copyright 2002-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.orm.jpa; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.TestInstantiationAwareExtension.ExtensionContextScope; +import org.junit.platform.suite.api.ConfigurationParameter; +import org.junit.platform.suite.api.SelectClasses; +import org.junit.platform.suite.api.Suite; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; +import org.springframework.test.context.orm.jpa.domain.Person; +import org.springframework.test.context.orm.jpa.domain.PersonRepository; +import org.springframework.transaction.annotation.Transactional; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test {@link Suite @Suite} which selects a single test class and runs it with + * {@link ExtensionContextScope#TEST_METHOD}. + * + * @author Sam Brannen + * @since 6.2.13 + * @see issue gh-34576 + */ +@Suite +@SelectClasses(JpaPersonRepositoryTests.TestCase.class) +@ConfigurationParameter( + key = ExtensionContextScope.DEFAULT_SCOPE_PROPERTY_NAME, + value = "test_method" // If this is changed to "default", NestedTests will fail. +) +// Even though this is a @Suite, it is intentionally named JpaPersonRepositoryTests +// instead of JpaPersonRepositoryTestSuite, so that it is run with the Gradle build +// due to the "*Tests" naming convention. +class JpaPersonRepositoryTests { + + /** + * Transactional tests for JPA support with {@link Nested @Nested} test classes. + */ + @SpringJUnitConfig(JpaConfig.class) + @Transactional + @Sql(statements = "insert into person(id, name) values(0, 'Jane')") + static class TestCase { + + @PersistenceContext + EntityManager em; + + @Autowired + PersonRepository repo; + + + @BeforeEach + void setup() { + em.persist(new Person("John")); + em.flush(); + } + + @Test + void findAll() { + assertThat(repo.findAll()).map(Person::getName).containsExactlyInAnyOrder("Jane", "John"); + } + + + @Nested + // Declare a random test property to ensure we get a different ApplicationContext. + @TestPropertySource(properties = "nested = true") + class NestedTests { + + @Test + void findAll() { + assertThat(repo.findAll()).map(Person::getName).containsExactlyInAnyOrder("Jane", "John"); + } + } + + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/orm/jpa/domain/JpaPersonRepository.java b/spring-test/src/test/java/org/springframework/test/context/orm/jpa/domain/JpaPersonRepository.java index cd4bf0d7379..2218279566f 100644 --- a/spring-test/src/test/java/org/springframework/test/context/orm/jpa/domain/JpaPersonRepository.java +++ b/spring-test/src/test/java/org/springframework/test/context/orm/jpa/domain/JpaPersonRepository.java @@ -16,6 +16,8 @@ package org.springframework.test.context.orm.jpa.domain; +import java.util.List; + import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; @@ -35,6 +37,12 @@ public class JpaPersonRepository implements PersonRepository { @PersistenceContext private EntityManager entityManager; + + @Override + public List findAll() { + return this.entityManager.createQuery("from Person", Person.class).getResultList(); + } + @Override public Person findById(Long id) { return this.entityManager.find(Person.class, id); diff --git a/spring-test/src/test/java/org/springframework/test/context/orm/jpa/domain/PersonRepository.java b/spring-test/src/test/java/org/springframework/test/context/orm/jpa/domain/PersonRepository.java index 111e966b249..6bb3f11ca32 100644 --- a/spring-test/src/test/java/org/springframework/test/context/orm/jpa/domain/PersonRepository.java +++ b/spring-test/src/test/java/org/springframework/test/context/orm/jpa/domain/PersonRepository.java @@ -16,6 +16,8 @@ package org.springframework.test.context.orm.jpa.domain; +import java.util.List; + /** * Person repository API. * @@ -24,6 +26,8 @@ package org.springframework.test.context.orm.jpa.domain; */ public interface PersonRepository { + List findAll(); + Person findById(Long id); Person findByName(String name);