Using Groovy & BeanBuilder for Spring + Hibernate Integration Testing

http://groovy.dzone.com/articles/using-groovy-beanbuilder-sprin

 

Using Groovy & BeanBuilder for Spring + Hibernate Integration Testingfficeffice" />

Submitted by Graeme Rocher on Wed, 2008/03/12 - 8:02am.

 

众所周知,用Groovy给你的java代码写单元测试有多棒。这得益于首类内置 lists, maps ranges组合起来支持通过groovy写的单元测试进行mocking,是一种非常好的方式来向java编程人员介绍Groovy

然而,集成测试是两码事,在Spring+Hibernate环境下,经常陷入in-memory数据库,Spring containers等组合中。在Spring中进行集成测试传统的方法是是继承(extend)诸如AbstractDependencyInjectionSpringContextTests,同时重写override获取本地配置的方法来提供静态的Spring XML configuration的使用。例如下面这个代码片段即使用了这种技术的Grails' tests

 

view plaincopy to clipboardprint?

1.                    protected String[] getConfigLocations() {  

2.                       return new String[] { "org/codehaus/groovy/grails/orm/hibernate/hibernate-mapped-class-tests.xml" };  

3.                   }   

     protected String[] getConfigLocations() {
        return new String[] { "org/codehaus/groovy/grails/orm/hibernate/hibernate-mapped-class-tests.xml" };
    }

 

The Spring XML 会完成注入任意的单例singleton beans的工作,所以你的类需要一个HibernateLocalSessionFactoryBean,内存数据源(, the in-memory DataSource)使用HSQLDB等类型的数据库。然而,所有这些是相当单调乏味的,你不得在你的测试中维持一个独立的XML文件。更为糟糕的是,要保证当你运行测试的时候,XML文件必须在in your build and in your IDE的类路径中(classpath)。结果自然是相当痛苦的。

 

好消息:使用Grails' BeanBuilder是一个更好的方法!

允许那些不知道BeanBuilderSpringDSL的人快速的使用类似Groovy builder的语法构建一个ApplicationContext应用程序上下文;

我们打算用它构建一个ApplicationContext应用程序上下文在集成测试中调用Hibernate

 

第一件事即需要创建一些Hibernate实体。之所以用Groovy是因为我们可以更好的使用Groovy中更为简洁的语法来书写代码

view plaincopy to clipboardprint?

1.                   package com.mycompany  

package com.mycompany

view plaincopy to clipboardprint?

1.                   import javax.persistence.*  

2.                   import org.hibernate.annotations.*  

3.                     

4.                   @Entity  

5.                   @Table(name="faq_section")  

6.                   class FaqSection  

7.                   {  

8.                       @Id  

9.                       @GeneratedValue(strategy = javax.persistence.GenerationType.IDENTITY)  

10.                   Long id  

11.                 

12.                   @Version  

13.                   Long version  

14.                 

15.                   String title  

16.                 

17.                   @OneToMany(cascade = [javax.persistence.CascadeType.ALL], targetEntity = FaqElement.class)  

18.                   @JoinColumn(name = "section_id", nullable = false)  

19.                   @IndexColumn(name = "pos", base = 0)  

20.                   List elements  

21.               }  

22.                 

23.               @Entity  

24.               @Table(name="faq_element")  

25.               class FaqElement  

26.               {  

27.                   @Id  

28.                   @GeneratedValue(strategy = javax.persistence.GenerationType.IDENTITY)  

29.                   Long id  

30.                 

31.                   @Version  

32.                   Long version  

33.                 

34.                   String question  

35.                   String answer  

36.                 

37.                   @ManyToOne  

38.                   @JoinColumn(name = "section_id", nullable = false, updatable = false, insertable = false)  

39.                   FaqSection section  

40.                 

41.               }  

 

此外JPA annotations有些碍眼,代码本身相当简洁,仅定义2个拥有one-to-many关联关系使用List集合的实体

做完后,配置Hibernate mapping

 

view plaincopy to clipboardprint?

1.                   <?xml version="1.0" encoding="UTF-8"?>  

2.                   <!DOCTYPE hibernate-configuration UBLIC  

3.                       "-//Hibernate/Hibernate Configuration DTD 3.0//EN"  

4.                       "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">  

5.                     

6.                   <hibernate-configuration>  

7.                     

8.                       <!-- a SessionFactory instance listed as /jndi/name -->  

9.                       <session-factory>  

10.                       <!-- mapping files -->  

11.                       <mapping class="com.mycompany.FaqSection" />  

12.                       <mapping class="com.mycompany.FaqElement" />  

13.                   </session-factory>  

14.                 

15.               </hibernate-configuration>  

 

 

 

我已经把这些保存在名为faq-service-hiberate.cfg.xml的文件,之后我们会用到的。当然如果你正在使用JPA可以创建一个persistence.xml配置。我的配置是直接使用Hibernate

现在做点有趣的,假设我们有FAQService接口和它的实现类FAQServiceImpl,简单的配置并装配用BeanBuilder测试中的setUp方法中。

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

<!-- a SessionFactory instance listed as /jndi/name -->
<session-factory>
<!-- mapping files -->
<mapping class="com.mycompany.FaqSection" />
        <mapping class="com.mycompany.FaqElement" />
    </session-factory>

</hibernate-configuration>

 

 

 

view plaincopy to clipboardprint?

1.                   import grails.spring.BeanBuilder  

2.                   import org.apache.commons.dbcp.BasicDataSource  

3.                   import org.hibernate.cfg.AnnotationConfiguration  

4.                   import org.hibernate.SessionFactory  

5.                   import com.mycompany.FAQService  

6.                   import com.mycompany.FAQServiceImpl  

7.                     

8.                   ...  

9.                     

10.                   FAQService faqService  

11.                 

12.                   protected void setUp() {  

13.                       def bb = new BeanBuilder()  

14.                 

15.                       bb.beans {  

16.                           dataSource(BasicDataSource) {  

17.                               url = "jdbc:hsqldb:mem:testDB"  

18.                               driverClassName = "org.hsqldb.jdbcDriver"  

19.                               username = "sa"  

20.                               password = ""  

21.                           }  

22.                           sessionFactory(LocalSessionFactoryBean) {  

23.                               dataSource = dataSource  

24.                               configLocation = "classpath:com/mycompany/test/faq-service-hiberate.cfg.xml"  

25.                               configurationClass = AnnotationConfiguration  

26.                               hibernateProperties = ["hibernate.hbm2ddl.auto":"create-drop"]  

27.                           }  

28.