使用内存数据库进行单元测试

本文介绍如何在Spring框架下使用H2内存数据库进行单元测试,通过配置数据源、实体管理工厂及事务管理器,并采用@ContextHierarchy分离测试上下文以减少测试开销。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:https://gerrydevstory.com/2014/04/11/unit-testing-using-mysql-in-memory-database-on-spring/

Unit Testing Using In-Memory MySQL Database On Spring

Well the title lied, there’s no such thing as in-memory MySQL database (or at least I won’t be using it for this article). Instead I will use H2 in-memory database setup to run in “MySQL mode”

?
1
2
3
4
< bean id = "dataSource" class = "org.apache.commons.dbcp.BasicDataSource" >
   < property name = "driverClassName" value = "org.h2.Driver" />
   < property name = "url" value = "jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE" />
</ bean >

(thanks to joensson for sharing this technique on SO)

If your app just uses plain jdbc then adding above datasource to your test context would be sufficient, but if you use JPA/Hibernate the cost of table setup, scanning etc could be quite significant.

To overcome this you can split the test context using @ContextHierarchy annotation.

In the example below I have unit tests for two DAOs: AccountDAOTest and CustomerDAOTest:

?
1
2
3
4
5
6
7
@ContextHierarchy ({
   @ContextConfiguration ( "/test-root-context.xml" ),
   @ContextConfiguration ( "AccountDAOTest-context.xml" )
})
@RunWith (SpringJUnit4ClassRunner. class )
public class AccountDAOTest {
}
?
1
2
3
4
5
6
7
@ContextHierarchy ({
   @ContextConfiguration ( "/test-root-context.xml" ),
   @ContextConfiguration ( "CustomerDAOTest-context.xml" )
})
@RunWith (SpringJUnit4ClassRunner. class )
public class CustomerDAOTest {
}

By doing this Spring test-root-context.xml will be setup once and reused accross all unit tests. Only put components common to all tests in test-root-context.xml. In my case I put the following:

  • DataSource
  • EntityManagerFactory
  • TransactionManager
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- JDBC Data Source -->
< bean id = "dataSource" class = "org.apache.commons.dbcp.BasicDataSource" >
   < property name = "driverClassName" value = "org.h2.Driver" />
   < property name = "url" value = "jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE" />
</ bean >
 
 
<!-- EntityManagerFactory -->
< bean class = "org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id = "entityManagerFactory" >
   < property name = "persistenceUnitName" value = "persistenceUnit" />
   < property name = "dataSource" ref = "dataSource" />
</ bean >
 
<!-- Transaction Manager -->
< bean class = "org.springframework.orm.jpa.JpaTransactionManager" id = "transactionManager" >
   < property name = "entityManagerFactory" ref = "entityManagerFactory" />
</ bean >

All test specific components go into their respective context.

Don’t forget to add <tx:annotation-driven/> if your DAO uses it. This can’t be placed on test-root-context.xml because I don’t scan all my DAOs there.

And lastly — ofcourse — you need to make sure your pom.xml has dependency to spring-test, junit and h2

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
< dependency >
   < groupId >com.h2database</ groupId >
   < artifactId >h2</ artifactId >
   < version >1.3.176</ version >
   < scope >test</ scope >
</ dependency >
 
< dependency >
   < groupId >org.springframework</ groupId >
   < artifactId >spring-test</ artifactId >
   < version >${org.springframework-version}</ version >
   < scope >test</ scope >
</ dependency >
 
< dependency >
   < groupId >junit</ groupId >
   < artifactId >junit</ artifactId >
   < version >4.7</ version >
   < scope >test</ scope >
</ dependency >

使用java代码进行bean的配置:

@Configuration
@EnableTransactionManagement(proxyTargetClass = true)
@EnableConfigurationProperties(value = {JpaProperties.class,DataSourceProperties.class})
public class MfgMasterInMemoryDBConfig {

// <!-- JDBC Data Source -->
// <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
// <property name="driverClassName" value="org.h2.Driver"/>
// <property name="url" value="jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE" />
// </bean>
//
//
// <!-- EntityManagerFactory -->
// <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
// <property name="persistenceUnitName" value="persistenceUnit" />
// <property name="dataSource" ref="dataSource" />
// </bean>
//
// <!-- Transaction Manager -->
// <bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
// <property name="entityManagerFactory" ref="entityManagerFactory" />
// </bean>

@Autowired
private JpaProperties jpaProperties;

@Bean
public JpaVendorAdapter jpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}

@Bean
public EntityManagerFactoryBuilder entityManagerFactoryBuilder() {
return new EntityManagerFactoryBuilder(jpaVendorAdapter(), jpaProperties.getProperties(), null);
}

@Bean
public EntityManager entityManger(EntityManagerFactory factory) {
return factory.createEntityManager();
}

@Bean
public DataSource dataSource(DataSourceProperties baseDataSourceProperties){
DataSource dataSource = DataSourceBuilder
.create(baseDataSourceProperties.getClassLoader())
.driverClassName("org.h2.Driver")
.url("jdbc:h2:mem:testdb;MODE=MySQL")
.username("sa")
.password("")
.build();;
return dataSource;
}

@Bean JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory){
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);
return jpaTransactionManager;
}

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, EntityManagerFactoryBuilder builder) {
Map<String, Object> hibernateProps = new LinkedHashMap<>();
hibernateProps.putAll(jpaProperties.getHibernateProperties(dataSource));

return builder
.dataSource(dataSource)
.packages(User.class.getPackage().getName())
.properties(hibernateProps).jta(false)
.build();
}
}

 

Test类的主要注解:


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={MfgMasterTestConfiguration.class,MfgMasterInMemoryDBConfig.class})
//@SpringApplicationConfiguration(classes = MfgMasterDataApplication.class)
public class MfgMasterDataApplicationTests {

@Autowired
public DataSource dataSource;

@Before
public void prepareUser(){
try {
dataSource.getConnection().createStatement().executeUpdate("INSERT INTO user (id, email, password, created_by, creation_date, enabled, update_date, updated_by, version, firstname, lastname, language) "
+ "VALUES (-1, 'system@mfg.com', 'system', -1, '2015-03-02 17:36:38', 0, NULL, NULL, 0, 'system', 'system', 'US')");
} catch (SQLException e) {
e.printStackTrace();
}
}

@Test
public void contextLoads() {
System.out.println("Done");
}

}

转载于:https://www.cnblogs.com/ly-radiata/p/5212676.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值