在 Spring 测试期间如何创建 CrudRepository 接口的实例?
How to make instance of CrudRepository interface during testing in Spring?
我有一个 Spring 应用程序,我在其中 不 使用 xml 配置,只使用 Java 配置。一切正常,但是当我尝试 test 时,我遇到了在测试中启用组件自动装配的问题。让我们开始吧。我有一个接口:
@Repository
public interface ArticleRepository extends CrudRepository<Page, Long> {
Article findByLink(String name);
void delete(Page page);
}
还有一个component/service:
@Service
public class ArticleServiceImpl implements ArticleService {
@Autowired
private ArticleRepository articleRepository;
...
}
我不想使用 xml 配置,因此对于我的测试,我尝试仅使用 Java 配置来测试 ArticleServiceImpl。所以为了测试目的我做了:
@Configuration
@ComponentScan(basePackages = {"com.example.core", "com.example.repository"})
public class PagesTestConfiguration {
@Bean
public ArticleRepository articleRepository() {
// (1) What to return ?
}
@Bean
public ArticleServiceImpl articleServiceImpl() {
ArticleServiceImpl articleServiceImpl = new ArticleServiceImpl();
articleServiceImpl.setArticleRepository(articleRepository());
return articleServiceImpl;
}
}
在 articleServiceImpl() 中,我需要放置 articleRepository() 的实例,但它是一个接口。如何使用 new 关键字创建新对象?是否可以不创建 xml 配置 class 并启用自动装配?在测试期间仅使用 Java 配置时是否可以启用自动装配?
您需要做的是:
从 ArticleRepository
中删除 @Repository
将@EnableJpaRepositories
添加到PagesTestConfiguration.java
@Configuration
@ComponentScan(basePackages = {"com.example.core"}) // are you sure you wanna scan all the packages?
@EnableJpaRepositories(basePackageClasses = ArticleRepository.class) // assuming you have all the spring data repo in the same package.
public class PagesTestConfiguration {
@Bean
public ArticleServiceImpl articleServiceImpl() {
ArticleServiceImpl articleServiceImpl = new ArticleServiceImpl();
return articleServiceImpl;
}
}
您不能在配置中使用存储库 class 因为从配置中 classes 它使用 @EnableJpaRepositories 找到所有存储库。
- 所以将您的 Java 配置更改为:
@Configuration
@EnableWebMvc
@EnableTransactionManagement
@ComponentScan("com.example")
@EnableJpaRepositories(basePackages={"com.example.jpa.repositories"})//Path of your CRUD repositories package
@PropertySource("classpath:application.properties")
public class JPAConfiguration {
//Includes jpaProperties(), jpaVendorAdapter(), transactionManager(), entityManagerFactory(), localContainerEntityManagerFactoryBean()
//and dataSource()
}
- 如果您有很多存储库实现 classes 然后创建一个单独的 class 如下所示
@Service
public class RepositoryImpl {
@Autowired
private UserRepositoryImpl userService;
}
- 在您的控制器中自动连接到 RepositoryImpl,您可以从那里访问所有存储库实现 classes.
@Autowired
RepositoryImpl repository;
用法:
repository.getUserService().findUserByUserName(userName);
删除 ArticleRepository 中的 @Repository 注解,ArticleServiceImpl 应该实现 ArticleRepository 而不是 ArticleService。
这是我发现的 spring 控制器测试的最小设置,它需要一个自动装配的 JPA 存储库配置(使用 spring-boot 1.2 和嵌入式 spring 4.1。 4.RELEASE, DbUnit 2.4.8)。
测试针对嵌入式 HSQL 数据库运行,该数据库在测试开始时由 xml 数据文件自动填充。
测试class:
@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration( classes = { TestController.class,
RepoFactory4Test.class } )
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionDbUnitTestExecutionListener.class } )
@DatabaseSetup( "classpath:FillTestData.xml" )
@DatabaseTearDown( "classpath:DbClean.xml" )
public class ControllerWithRepositoryTest
{
@Autowired
private TestController myClassUnderTest;
@Test
public void test()
{
Iterable<EUser> list = myClassUnderTest.findAll();
if ( list == null || !list.iterator().hasNext() )
{
Assert.fail( "No users found" );
}
else
{
for ( EUser eUser : list )
{
System.out.println( "Found user: " + eUser );
}
}
}
@Component
static class TestController
{
@Autowired
private UserRepository myUserRepo;
/**
* @return
*/
public Iterable<EUser> findAll()
{
return myUserRepo.findAll();
}
}
}
备注:
@ContextConfiguration注解只包含内嵌的TestController和JPA配置class RepoFactory4Test.
需要@TestExecutionListeners注解才能使后续注解@DatabaseSetup和@DatabaseTearDown生效
引用配置class:
@Configuration
@EnableJpaRepositories( basePackageClasses = UserRepository.class )
public class RepoFactory4Test
{
@Bean
public DataSource dataSource()
{
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType( EmbeddedDatabaseType.HSQL ).build();
}
@Bean
public EntityManagerFactory entityManagerFactory()
{
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl( true );
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter( vendorAdapter );
factory.setPackagesToScan( EUser.class.getPackage().getName() );
factory.setDataSource( dataSource() );
factory.afterPropertiesSet();
return factory.getObject();
}
@Bean
public PlatformTransactionManager transactionManager()
{
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory( entityManagerFactory() );
return txManager;
}
}
UserRepository 是一个简单的接口:
public interface UserRepository extends CrudRepository<EUser, Long>
{
}
EUser 是一个简单的 @Entity 注释 class:
@Entity
@Table(name = "user")
public class EUser
{
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
@Max( value=Integer.MAX_VALUE )
private Long myId;
@Column(name = "email")
@Size(max=64)
@NotNull
private String myEmail;
...
}
FillTestData.xml:
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<user id="1"
email="alice@test.org"
...
/>
</dataset>
DbClean.xml:
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<user />
</dataset>
如果您使用 Spring 启动,您可以通过添加 @SpringBootTest
加载到 ApplicationContext
来稍微简化这些方法。这允许您在 spring 数据存储库中自动装配。请务必添加 @RunWith(SpringRunner.class)
,以便选择 spring-特定注释:
@RunWith(SpringRunner.class)
@SpringBootTest
public class OrphanManagementTest {
@Autowired
private UserRepository userRepository;
@Test
public void saveTest() {
User user = new User("Tom");
userRepository.save(user);
Assert.assertNotNull(userRepository.findOne("Tom"));
}
}
您可以在他们的 docs.
中阅读有关 spring 引导中测试的更多信息
我有一个 Spring 应用程序,我在其中 不 使用 xml 配置,只使用 Java 配置。一切正常,但是当我尝试 test 时,我遇到了在测试中启用组件自动装配的问题。让我们开始吧。我有一个接口:
@Repository
public interface ArticleRepository extends CrudRepository<Page, Long> {
Article findByLink(String name);
void delete(Page page);
}
还有一个component/service:
@Service
public class ArticleServiceImpl implements ArticleService {
@Autowired
private ArticleRepository articleRepository;
...
}
我不想使用 xml 配置,因此对于我的测试,我尝试仅使用 Java 配置来测试 ArticleServiceImpl。所以为了测试目的我做了:
@Configuration
@ComponentScan(basePackages = {"com.example.core", "com.example.repository"})
public class PagesTestConfiguration {
@Bean
public ArticleRepository articleRepository() {
// (1) What to return ?
}
@Bean
public ArticleServiceImpl articleServiceImpl() {
ArticleServiceImpl articleServiceImpl = new ArticleServiceImpl();
articleServiceImpl.setArticleRepository(articleRepository());
return articleServiceImpl;
}
}
在 articleServiceImpl() 中,我需要放置 articleRepository() 的实例,但它是一个接口。如何使用 new 关键字创建新对象?是否可以不创建 xml 配置 class 并启用自动装配?在测试期间仅使用 Java 配置时是否可以启用自动装配?
您需要做的是:
从
ArticleRepository
中删除 将
@EnableJpaRepositories
添加到PagesTestConfiguration.java
@Configuration @ComponentScan(basePackages = {"com.example.core"}) // are you sure you wanna scan all the packages? @EnableJpaRepositories(basePackageClasses = ArticleRepository.class) // assuming you have all the spring data repo in the same package. public class PagesTestConfiguration { @Bean public ArticleServiceImpl articleServiceImpl() { ArticleServiceImpl articleServiceImpl = new ArticleServiceImpl(); return articleServiceImpl; } }
@Repository
您不能在配置中使用存储库 class 因为从配置中 classes 它使用 @EnableJpaRepositories 找到所有存储库。
- 所以将您的 Java 配置更改为:
@Configuration @EnableWebMvc @EnableTransactionManagement @ComponentScan("com.example") @EnableJpaRepositories(basePackages={"com.example.jpa.repositories"})//Path of your CRUD repositories package @PropertySource("classpath:application.properties") public class JPAConfiguration { //Includes jpaProperties(), jpaVendorAdapter(), transactionManager(), entityManagerFactory(), localContainerEntityManagerFactoryBean() //and dataSource() }
- 如果您有很多存储库实现 classes 然后创建一个单独的 class 如下所示
@Service public class RepositoryImpl { @Autowired private UserRepositoryImpl userService; }
- 在您的控制器中自动连接到 RepositoryImpl,您可以从那里访问所有存储库实现 classes.
@Autowired RepositoryImpl repository;
用法:
repository.getUserService().findUserByUserName(userName);
删除 ArticleRepository 中的 @Repository 注解,ArticleServiceImpl 应该实现 ArticleRepository 而不是 ArticleService。
这是我发现的 spring 控制器测试的最小设置,它需要一个自动装配的 JPA 存储库配置(使用 spring-boot 1.2 和嵌入式 spring 4.1。 4.RELEASE, DbUnit 2.4.8)。
测试针对嵌入式 HSQL 数据库运行,该数据库在测试开始时由 xml 数据文件自动填充。
测试class:
@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration( classes = { TestController.class,
RepoFactory4Test.class } )
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionDbUnitTestExecutionListener.class } )
@DatabaseSetup( "classpath:FillTestData.xml" )
@DatabaseTearDown( "classpath:DbClean.xml" )
public class ControllerWithRepositoryTest
{
@Autowired
private TestController myClassUnderTest;
@Test
public void test()
{
Iterable<EUser> list = myClassUnderTest.findAll();
if ( list == null || !list.iterator().hasNext() )
{
Assert.fail( "No users found" );
}
else
{
for ( EUser eUser : list )
{
System.out.println( "Found user: " + eUser );
}
}
}
@Component
static class TestController
{
@Autowired
private UserRepository myUserRepo;
/**
* @return
*/
public Iterable<EUser> findAll()
{
return myUserRepo.findAll();
}
}
}
备注:
@ContextConfiguration注解只包含内嵌的TestController和JPA配置class RepoFactory4Test.
需要@TestExecutionListeners注解才能使后续注解@DatabaseSetup和@DatabaseTearDown生效
引用配置class:
@Configuration
@EnableJpaRepositories( basePackageClasses = UserRepository.class )
public class RepoFactory4Test
{
@Bean
public DataSource dataSource()
{
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType( EmbeddedDatabaseType.HSQL ).build();
}
@Bean
public EntityManagerFactory entityManagerFactory()
{
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl( true );
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter( vendorAdapter );
factory.setPackagesToScan( EUser.class.getPackage().getName() );
factory.setDataSource( dataSource() );
factory.afterPropertiesSet();
return factory.getObject();
}
@Bean
public PlatformTransactionManager transactionManager()
{
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory( entityManagerFactory() );
return txManager;
}
}
UserRepository 是一个简单的接口:
public interface UserRepository extends CrudRepository<EUser, Long>
{
}
EUser 是一个简单的 @Entity 注释 class:
@Entity
@Table(name = "user")
public class EUser
{
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
@Max( value=Integer.MAX_VALUE )
private Long myId;
@Column(name = "email")
@Size(max=64)
@NotNull
private String myEmail;
...
}
FillTestData.xml:
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<user id="1"
email="alice@test.org"
...
/>
</dataset>
DbClean.xml:
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<user />
</dataset>
如果您使用 Spring 启动,您可以通过添加 @SpringBootTest
加载到 ApplicationContext
来稍微简化这些方法。这允许您在 spring 数据存储库中自动装配。请务必添加 @RunWith(SpringRunner.class)
,以便选择 spring-特定注释:
@RunWith(SpringRunner.class)
@SpringBootTest
public class OrphanManagementTest {
@Autowired
private UserRepository userRepository;
@Test
public void saveTest() {
User user = new User("Tom");
userRepository.save(user);
Assert.assertNotNull(userRepository.findOne("Tom"));
}
}
您可以在他们的 docs.
中阅读有关 spring 引导中测试的更多信息