`

spring中的单元测试的策略

阅读更多
本文主要介绍使用spring提供的对junit的扩展机制来进行单元测试,没有设计mock方面的测试。

一、Spring提供的JUnit框架扩展:

1.AbstractSpringContextTests:spring中使用spring上下文测试的Junit扩展类,我们一般不会使用这个类来进行单元测试,它是spring内部设计使用到的类
2.AbstractDependencyInjectionSpringContextTests:这是AbstractSpringContextTests的直接子类,支持依赖spring上下文的测试类,这个类不支持事务。
3.AbstractTransactionalSpringContextTests:这是AbstractDependencyInjectionSpringContextTests的直接子类,这个类一般应用在事务相关的测试中,一旦完成每个测试它就会正常地回滚事务,不会真正更新数据库,若要手动设置事务相关操作,你可以重载onSetUpInTransaction和onTearDownInTransaction方法,以便手工开始并提交事务,或者调用setComplete()方法。这个类也可以在没有事务的情况下,使用这个类。
4.AbstractTransactionalDataSourceSpringContextTests:这是AbstractTransactionalSpringContextTests的直接子类,它使用了Spring的基于JDBC的jdbcTemplate工具类,支持数据库级别的事务。
我是按照继承顺序介绍上面四个类的,具体可以参考spring的api文档,或者可以阅读spring的源代码,很简单的代码。

二、测试策略:

下面用一个真实项目的简化版本来作例子,测试的具体目录路径:

test
    --system
        --dao
            --UserDAOImplTest.java
            ......
        --service
            --UserServiceImplTest.java
            ......
        --AbstractSystemTest.java
        --testSystemContext.xml
    --manage
        --dao
            --CustomerDAOImplTest.java
            ......
        --service
            --CustomerServiceImplTest.java
            ......
        --AbstractManageTest.java
        --testSystemContext.xml
    --testApplicationContext.xml
解释:
test是测试路径,system与manage是模块名(这本来是package的路径,为了简化,我就把前面的包名删除了),每个模块中有一个spring的配置文件(我的spring配置文件的命名策略是test+模块名+Context),一个模块级别测试基类(主要是载入spring配置文件,每个该模块的单元测试类都继承该类),然后是分层路径,这里是DAO和service层,包括所有的DAO与service单元测试类,我们还有一个总的spring配置文件testApplicationContext.xml。再多的解释,也不如代码最明显,下面是部分示例代码
testApplicationContext.xml
    <bean id="propertyConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>jdbc.properties</value>
            </list>
        </property>
    </bean>

    <bean id="dataSource"
        class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName">
            <value>${jdbc.driverClassName}</value>
        </property>
        <property name="url">
            <value>${jdbc.url}</value>
        </property>
        <property name="username">
            <value>${jdbc.username}</value>
        </property>
        <property name="password">
            <value>${jdbc.password}</value>
        </property>
    </bean>

    <bean id="parentSessionFactory" abstract="true"
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">
                    org.hibernate.dialect.Oracle9Dialect
                </prop>
                <prop key="hibernate.cache.provider_class">
                    org.hibernate.cache.EhCacheProvider
                </prop>
                <prop key="hibernate.max_fetch_depth">3</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.cglib.use_reflection_optimizer">
                    true
                </prop>
            </props>
        </property>
        <property name="dataSource">
            <ref local="dataSource" />
        </property>
    </bean>

    <!-- transaction -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <bean id="baseTransactionProxy"
        class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
        abstract="true">
        <property name="transactionManager" ref="transactionManager" />
        <property name="transactionAttributes">
            <props>
                <prop key="*">
                    PROPAGATION_REQUIRED, -ApplicationException
                </prop>
                <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
                <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
                <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
                <prop key="list*">PROPAGATION_REQUIRED,readOnly</prop>
            </props>
        </property>
    </bean>

    <!-- Base DAO -->
    <bean id="baseDAO" abstract="true"
        class="com.mycompany.myproject.framework.BaseDAOImpl">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

所有的spring的基本配置文件都放在这里面,我巧妙的把利用spring提供的配置技巧声明了一个抽象的parentSessionFactory,然后让每个子模块的sessionFactory都继承这个对象,这样就减少了不少重复的配置文件

testSystemContext.xml
    <bean id="sessionFactory" parent="parentSessionFactory"
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="mappingDirectoryLocations">
            <list>
                <value>
                    com/mycompany/myproject/domain/system/maps
                </value>
            </list>
        </property>
    </bean>
    
    <!-- DAO -->
    <bean id="userDAO" parent="baseDAO"
        class="com.mycompany.myproject.module.system.dao.UserDAOImpl">
        <constructor-arg>
            <value>com.mycompany.myproject.domain.system.User</value>
        </constructor-arg>
    </bean>

    <!-- Service -->
    <bean id="userService" parent="baseTransactionProxy">
        <property name="target">
            <bean
                class="com.mycompany.myproject.module.system.service.UserServiceImpl">
                <property name="userDAO" ref="userDAO" />
            </bean>
        </property>
    </bean>

在这个文件中,声明了一个继承parentSessionFactory的sessionFactory,只需配置mappingDirectoryLocations

AbstractSystemTest.java
public abstract class AbstractSystemTest extends
        AbstractTransactionalDataSourceSpringContextTests ...{

    protected String[] getConfigLocations() ...{
        String[] config = new String[] ...{ "testApplicationContext.xml",
                "system/testSystemContext.xml" };
        return config;
    }

}

覆盖基类的方法,提供寻找配置文件的功能,自己可以根据情况把基类改为AbstractTransactionalSpringContextTests

UserDAOImplTest.java
public class UserDAOImplTest extends AbstractSystemTest ...{

    public UserDAO getUserDAO() ...{
        return (UserDAO) applicationContext.getBean("userDAO");
    }

    public void testLoadObject() ...{
        User user = (User) this.getUserDAO().find("01");

        assertNull(user);
    }
}

UserServiceImplTest.java
public class UserServiceImplTest extends AbstractSystemTest ...{

    public UserService getUserService() ...{
        return (UserService) applicationContext.getBean("userService");
    }

    public void testLoadUser() ...{
        User user = (User) this.getUserService().find("01");

        assertNull(user);
    }
}

我的基本策略,就是让让每个开发人员写的单元测试之间不会相互干扰,其实,在spring中的关键问题时把载入context配置文件相互不干扰。上面的示例表明,采用spring提供的配置文件可以轻松达到这个目的
但是,到了项目中后期的时候,会发现service经常需要访问不属于自己模块的DAO,或者是domain对象的关联被映射在hibernate配置文件中,这时会发现这个方式很耗时,因为你需要导入别的模块的配置文件,这时可以采用一个sessionFacotry的配置,而且这时domain对象也很稳定了,不会经常出现
unmmaped class的异常。
写一篇文章真是很痛苦,搞了5个小时,真消耗时间。
分享到:
评论

相关推荐

    Spring-Reference_zh_CN(Spring中文参考手册)

    8.2. 单元测试 8.3. 集成测试 8.3.1. Context管理和缓存 8.3.2. 测试fixture的依赖注入 8.3.3. 事务管理 8.3.4. 方便的变量 8.3.5. 示例 8.3.6. 运行集成测试 8.4. 更多资源 II. 中间层数据访问 9. 事务管理 9.1. ...

    Spring中文帮助文档

    8.2.2. 单元测试支持类 8.3. 集成测试 8.3.1. 概览 8.3.2. 使用哪个支持框架 8.3.3. 通用目标 8.3.4. JDBC测试支持 8.3.5. 常用注解 8.3.6. JUnit 3.8遗留支持 8.3.7. Spring TestContext Framework 8.3.8....

    Spring高级之注解驱动开发视频教程

    SpringTest它是针对Junit单元测试的整合。让我们在开发中以及开发后期进行测试时,直接使用Junit结合spring一起测试。 本套课程中,我们将全面剖析Spring和SpringMVC两个部分。从应用场景分析,到基本用法的入门...

    Spring 2.0 开发参考手册

    8.2. 单元测试 8.3. 集成测试 8.3.1. Context管理和缓存 8.3.2. 测试fixture的依赖注入 8.3.3. 事务管理 8.3.4. 方便的变量 8.3.5. 示例 8.3.6. 运行集成测试 8.4. 更多资源 II. 中间层数据访问 9. 事务...

    spring-boot-testing-strategies:示例项目,展示了使用Spring Boot时可以遵循的不同测试策略

    使用Spring Boot制作的这个示例应用程序旨在展示不同的测试方法,从独立模式下的MockMVC单元测试到模块之间的集成测试(完整@SpringBootTest 。 完整的指南可在。 应用程序 该应用程序背后的逻辑很简单:它是超级...

    Spring API

    8.2.2. 单元测试支持类 8.3. 集成测试 8.3.1. 概览 8.3.2. 使用哪个支持框架 8.3.3. 通用目标 8.3.4. JDBC测试支持 8.3.5. 常用注解 8.3.6. JUnit 3.8遗留支持 8.3.7. Spring TestContext Framework 8.3.8....

    spring chm文档

    8.2. 单元测试 8.3. 集成测试 8.3.1. Context管理和缓存 8.3.2. 测试fixture的依赖注入 8.3.3. 事务管理 8.3.4. 方便的变量 8.3.5. 示例 8.3.6. 运行集成测试 8.4. 更多资源 II. 中间层数据访问 9. 事务...

    spring-boot-microservice-template:Spring Boot微服务模板,基于持续交付的思想,用于快速交付可持续交付的微服务

    Spring Boot MicroService模板技术栈代码规范配置管理测试... 单元测试 组件测试 api测试 测试覆盖率 合同测试 整合测试 性能测试 测试覆盖率应用层 DDD策略 DIP(弹簧容器) AOP(SpringAOP) ORM(mybatis) Asnyc

    JavaEE求职简历-姓名-JAVA开发工程师.docx

    5.熟悉基于Junit的单元测试,熟悉Dbunit,EasyMock;了解测试驱动编程; 6.web前端技术:熟悉HTML、CSS、JavaScript;有在项目中使用过jQuery、Ajax、Vue.js、Bootstrap、ECharts等; 7.熟悉MySQL、Oracle,熟练使用...

    基于SSH的学生信息管理系统-实训个人工作总结.doc

    《基于SSH的学生信息管理系统-实训个人工作总结.doc》是一份...测试和调试部分,总结可能会强调个人对系统的测试策略和测试用例的制定,以及如何进行单元测试、集成测试和验收测试。可能会详细讲述个人如何发现并修复代

    JavaEE求职简历-姓名-JAVA开发工程师-3年经验.docx

    熟练使用 Java 基础知识,有良好的编码习惯,熟练使用常用的 Java API。...&gt; 熟悉 Junit,dbunit,EasyMock 技术,对项目进行单元测试和集成测试。 &gt; 熟悉 Netfix 策略实现分布式、实现模块间的通信,

    pact-example:示例项目,以通过Pact演示合同测试

    有效的测试套件包括多种测试策略,这些策略可导致较高的测试覆盖率,从而提高信心。 这些策略通常采用单元,组件,集成和验收测试的形式。 监控和警报。 测试套件需要快速运行,并且可靠。 这还不够强调。 如果测试...

    test-practice

    微服务测试策略 用Spring技术栈编写的简单微服务测试策略。 单元测试 整合测试 合同测试(提供商和消费者合同测试) UI驱动的端到端测试 RestAPI驱动的端到端测试

    Grails 中文参考手册

    9.1 单元测试 9.2 集成测试 9.3 功能测试 10. 国际化 10.1 理解信息绑定 10.2 改变Locales 10.3 读取信息 11. 安全 11.1 预防攻击 11.2 字符串的编码和解码 11.3 身份验证 11.4 关于安全的插件 11.4.1 Acegi 11.4.2 ...

    基于SpringBoot的网络海鲜市场系统的设计与实现.zip

    测试用例:编写单元测试和集成测试来验证系统的稳定性和可靠性,确保代码质量。部署说明:虽然不包含安装步骤,但通常会有部署环境的搭建指南,指导用户如何将系统部署到服务器上。用户文档:提供用户手册或帮助文档...

    automation-test-engine:没有 OSGI 框架的独立 Selenium ATE

    当前版本的 ATE 是一个基于 Selenium 构建的开源工具,用于系统级(端到端)QA 回归测试,而不是用于开发人员单元测试。 它是第一个解决了易用性与代码可维护性冲突的工具,市场上大多数系统QA自动化工具包括QTP、...

    达内java培训目录

    企业应用开发部署环境 Linux高级命令集脚本编程、远程登录、Ant、单元测试技术、Maven构建技术、SVN应用技术。 熟练掌握基于Linux系统的操作技能;可以熟练的完成应用的部署工作;可以熟练的使用开发部署工具。 ...

    iBATIS实战

    13.1 iBATIS中的单元测试 230 13.1.1 对映射层进行单元测试 231 13.1.2 对DAO进行单元测试 233 13.1.3 对DAO的消费层进行单元测试 235 13.2 管理iBATIS配置文件 237 13.2.1 将其保存在类路径上 237 13.2.2 集中放置...

    股票指标源码java-stocks-api:简单股票SpringBoot应用程序

    该应用程序包含主要的应用程序源代码和单元测试。 集成测试模块包含在 Web 服务级别进行的端到端测试。 建造 project$ mvn clean install 跑步 project$ cd application project$ mvn spring-boot:run 出于验证目的...

Global site tag (gtag.js) - Google Analytics