基于spring test框架进行单元测试-框架介绍

什么是Spring TestContext? 

      Spring TestContext是Spring提供的一套基于注解的Test框架,Spring TestContext有非常好的兼容性,可以无缝兼容JUnit,TestNG等单元测试框架,而且在其基础上增加更多的功能 

      在Spring应用大行其道的今天,使用Spring来构建应用已经是再普通不过的事情,但当使用JUnit,TestNG等传统的单元测试技术和Spring进行结合的时候,就会出现很多并不如意的事情  

  • 1.Spring容器初始化问题:此问题已于JUnit4中被解决,通过使用@BeforeClass 可以有效防止Spring容器被多次初始化的问题 
  •  2.硬编码获取Bean:此问题是由于JUnit并不兼容Spring,所以当单元测试运行的时候,无法解释Spring独有的注解,从而需要使用硬编码来获取Bean 
  •  3.数据现场破坏:JUnit当中可以使用DBUnit来进行数据现场维护的解决方案, 详情可以通过笔者的DBUnit使用了解DBUnit,Spring TestContext通过AOP声明式事务来对单元测试进行回滚,有效的解决了数据现场的问题 
  •  4.事务:通常我们的单元测试都需要和数据库进行交互,但传统的JUnit的组成单元为TestCase,并不存在事务的概念,而我们大多数情况下都需要观察事务的执行过程或总体的性能,特别是对长事务模块的测试, Spring TestContext允许单元测试支持事务的控制 

      还好,Spring 提供了单元测试的强大支持,主要特性包括:

  • 支持主流的测试框架 Junit 和 TestNG
  • 支持在测试类中使用依赖注入 Denpendency Injection
  • 支持测试类的自动化事务管理
  • 支持使用各种注释标签,提高开发效率和代码简洁性
  • Spring 3.1 更是支持在测试类中使用非 XML 配置方法和基于 Profile 的 bean 配置模式

Spring TestContext 测试框架体系结构

 Spring TestContext 测试框架核心类

 

基于spring test框架进行单元测试-框架介绍_第1张图片

 TestContext 测试框架的核心由 org.springframework.test.context 包中三个类组成,分别是 TestContext  TestContextManager 类以及 TestExecutionListener 接口。

  • TestContext:它封装了运行测试用例的上下文,与当前使用的测试框架无关。
  • TestContextManager:它是进入 Spring TestContext 框架的程序主入口,它管理着一个 TestContext 实例,并在适合的执行点上向所有注册在 TestContextManager 中的TestExecutionListener 监听器发布事件通知:比如测试用例实例的准备,测试方法执行前后方法的调用等。
  • TestExecutionListener:该接口负责响应 TestContextManager 发布的事件。

Spring TestContext 允许在测试用例类中通过 @TestExecutionListeners 注解向 TestContextManager 注册多个监听器

@TestExecutionListeners ( {
     DependencyInjectionTestExecutionListener. class ,
     DirtiesContextTestExecutionListener. class  })
public  class  xxxServiceTest{
    
}

Spring 提供了几个 TestExecutionListener 接口实现类,分别说明如下:

  • DependencyInjectionTestExecutionListener:该监听器提供了自动注入的功能,它负责解析测试用例中 @Autowried 注解并完成自动注入;
  • DirtiesContextTestExecutionListener:一般情况下测试方法并不会对 Spring 容器上下文造成破坏(改变 Bean 的配置信息等),如果某个测试方法确实会破坏 Spring 容器上下文,你可以显式地为该测试方法添加 @DirtiesContext 注解,以便 Spring TestContext 在测试该方法后刷新 Spring 容器的上下文,而 DirtiesContextTestExecutionListener 监听器的工作就是解析 @DirtiesContext 注解;
  • TransactionalTestExecutionListener:它负责解析 @Transaction、@NotTransactional 以及 @Rollback 等事务注解的注解。@Transaction 注解让测试方法工作于事务环境中,不过在测试方法返回前事务会被回滚。你可以使用 @Rollback(false) 让测试方法返回前提交事务。而 @NotTransactional 注解则让测试方法不工作于事务环境中。此外,你还可以使用类或方法级别的 @TransactionConfiguration 注解改变事务管理策略。


 JUnit 4 中可以通过 @RunWith 注解指定测试用例的运行器,Spring TestContext 框架提供了扩展于 org.junit.internal.runners.JUnit4ClassRunner 的 SpringJUnit4ClassRunner 运行器,它负责总装 Spring TestContext 测试框架并将其统一到 JUnit 4 框架中。

TestContext 提供的抽象测试用例类

Spring TestContext 为基于 JUnit 4.4 测试框架提供了两个抽象测试用例类,分别是 AbstractJUnit4SpringContextTests 和 AbstractTransactionalJUnit4SpringContextTests,而后者扩展于前者。让我们来看一下这两个抽象测试用例类的代码:

@RunWith(SpringJUnit4ClassRunner.class)  //指定测试用例运行器
//注册了两个TestExecutionListener监听器
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class })
public abstract class AbstractJUnit4SpringContextTests implements ApplicationContextAware {
    /**
     * Logger available to subclasses.
     */
    protected final Log logger = LogFactory.getLog(getClass());
    /**
     * The {@link ApplicationContext} that was injected into this test instance
     * via {@link #setApplicationContext(ApplicationContext)}.
     */
    protected ApplicationContext applicationContext;
 
    /**
     * Set the {@link ApplicationContext} to be used by this test instance,
     * provided via {@link ApplicationContextAware} semantics.
     */
    public final void setApplicationContext(final ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
}

RunWith注解 将 SpringJUnit4ClassRunner 指定为测试用例运行器,它负责无缝地将 TestContext 测试框架移花接木到 JUnit 测试框架中,它是 Spring TestContext 可以运行起来的根本所在。@TestExecutionListeners 注解向测试用例类中注册了两个 TestExecutionListener 监听器,这两个监听器分别负责对 @Autowired 和 @DirtiesContext 注解进行处理,为测试用例提供自动注入和重新刷新 Spring 容器上下文的功能。

AbstractTransactionalJUnit4SpringContextTests 扩展于 AbstractJUnit4SpringContextTests,提供了事务管理的支持

//注册测试用例事务管理的监听器
@TestExecutionListeners(TransactionalTestExecutionListener.class)
//使测试用例的所有方法都将工作于事务环境下
@Transactional
@SuppressWarnings("deprecation")
public abstract class AbstractTransactionalJUnit4SpringContextTests extends AbstractJUnit4SpringContextTests {
    /**
     * The {@code SimpleJdbcTemplate} that this base class manages, available to subclasses.
     * @deprecated As of Spring 3.2, use {@link #jdbcTemplate} instead.
     */
    @Deprecated
    protected SimpleJdbcTemplate simpleJdbcTemplate;
    /**
     * The {@code JdbcTemplate} that this base class manages, available to subclasses.
     * @since 3.2
     */
    protected JdbcTemplate jdbcTemplate;
    private String sqlScriptEncoding;
 
    /**
     * Set the {@code DataSource}, typically provided via Dependency Injection.
     * 

This method also instantiates the {@link #simpleJdbcTemplate} and * {@link #jdbcTemplate} instance variables. */ @Autowired public void setDataSource(DataSource dataSource) { this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource); this.jdbcTemplate = new JdbcTemplate(dataSource); } /** * Specify the encoding for SQL scripts, if different from the platform encoding. * @see #executeSqlScript */ public void setSqlScriptEncoding(String sqlScriptEncoding) { this.sqlScriptEncoding = sqlScriptEncoding; } /** * Count the rows in the given table. * @param tableName table name to count rows in * @return the number of rows in the table */ protected int countRowsInTable(String tableName) { return JdbcTestUtils.countRowsInTable(this.jdbcTemplate, tableName); } /** * Count the rows in the given table, using the provided {@code WHERE} clause. *

See the Javadoc for {@link JdbcTestUtils#countRowsInTableWhere} for details. * @param tableName the name of the table to count rows in * @param whereClause the {@code WHERE} clause to append to the query * @return the number of rows in the table that match the provided * {@code WHERE} clause * @since 3.2 */ protected int countRowsInTableWhere(String tableName, String whereClause) { return JdbcTestUtils.countRowsInTableWhere(this.jdbcTemplate, tableName, whereClause); } /** * Convenience method for deleting all rows from the specified tables. Use * with caution outside of a transaction! * @param names the names of the tables from which to delete * @return the total number of rows deleted from all specified tables */ protected int deleteFromTables(String... names) { return JdbcTestUtils.deleteFromTables(this.jdbcTemplate, names); } /** * Convenience method for dropping all of the specified tables. Use * with caution outside of a transaction! * @param names the names of the tables to drop * @since 3.2 */ protected void dropTables(String... names) { JdbcTestUtils.dropTables(this.jdbcTemplate, names); } /** * Execute the given SQL script. Use with caution outside of a transaction! *

The script will normally be loaded by classpath. There should be one * statement per line. Any semicolons will be removed. Do not use this * method to execute DDL if you expect rollback. * @param sqlResourcePath the Spring resource path for the SQL script * @param continueOnError whether or not to continue without throwing an * exception in the event of an error * @throws DataAccessException if there is an error executing a statement * and continueOnError was {@code false} */ protected void executeSqlScript(String sqlResourcePath, boolean continueOnError) throws DataAccessException { Resource resource = this.applicationContext.getResource(sqlResourcePath); JdbcTestUtils.executeSqlScript(this.jdbcTemplate, new EncodedResource(resource, this.sqlScriptEncoding), continueOnError); } }



引入对junit以及spring-test库的依赖


    junit
    junit
    ${junitVersion}


    org.springframework
    spring-test
    ${org.springframework-version}
    test

Spring 的 TestContext 测试框架不但可以整合到 JUnit 4.4 测试框架上,而且还可以整合到 JUnit 3.8 以及 TestNG 等测试框架上。目前已经提供了对 JUnit 3.8 以及 TestNG 的支持,你可以分别在 org.springframework.test.context.junit38 和 org.springframework.test.context.testng 包下找到整合的帮助类。


你可能感兴趣的:(Java,spring)