【问题标题】:Spring: unit and integration testsSpring:单元和集成测试
【发布时间】:2011-01-04 12:25:30
【问题描述】:

我正在寻找使用 Spring 设置单元和集成测试的最佳实践。

我通常使用 3 种测试:

  • “真实”单元测试(无依赖性)
  • 测试作为“单元”测试运行(内存数据库、本地调用、模拟 对象,...)或作为集成测试 (持久化数据库、远程调用...)
  • 测试仅作为集成测试运行

目前我只有第二类的测试,这是棘手的部分。 我设置了一个基本测试类,例如:

@ContextConfiguration(locations = { "/my_spring_test.xml" })
public abstract class AbstractMyTestCase extends AbstractJUnit4SpringContextTests

还有“单元”测试,例如:

public class FooTest extends AbstractMyTestCase

具有自动装配的属性。

在不同的(集成测试)环境中运行测试的最佳方法是什么?子类化测试并覆盖 ContextConfiguration?

@ContextConfiguration(locations = { "/my_spring_integration_test.xml" })
public class FooIntegrationTest extends FooTest

这行得通吗(我目前无法在这里轻松测试)?这种方法的问题是“@ContextConfiguration(locations = { "/my_spring_integration_test.xml" })" 重复很多。

有什么建议吗?

问候, 弗洛里安

【问题讨论】:

  • 您找到适合您的解决方案了吗?

标签: java unit-testing spring maven-2 integration-testing


【解决方案1】:

我扩展了GenericXmlContextLoader

public class MyContextLoader extends GenericXmlContextLoader {

并重写

protected String[] generateDefaultLocations(Class<?> clazz)

收集目录的配置文件名的方法,我可以通过 SystemProperty (-Dtest.config=) 指定。

我还修改了以下方法以不修改任何位置

@Override
protected String[] modifyLocations(Class<?> clazz, String... locations) {
    return locations;
}

我像这样使用这个上下文加载器

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = MyContextLoader.class)
public class Test { .... }

使用指示配置文件来源的 SystemProperty 运行测试,您现在可以使用完全不同的配置。

SystemProperty 的使用当然只是指定配置位置的一种策略。您可以在generateDefaultLocations() 中为所欲为。


编辑:

此解决方案使您能够使用完全不同的应用程序上下文配置(例如,用于模拟对象),而不仅仅是不同的属性。您不需要构建步骤即可将所有内容部署到“类路径”位置。如果没有给出系统属性,我的具体实现还默认使用用户名来查找配置目录(src/test/resources/{user})(便于为项目中的所有开发人员维护特定的测试环境)。

PropertyPlaceholder 的使用仍然是可能的并且推荐使用。


编辑

Spring 版本 3.1.0 将支持 XML profiles/Environment Abstraction,这与我的解决方案类似,可以为不同的环境/配置文件选择配置文件。

【讨论】:

  • 这听起来很有趣。谢谢!
  • @Puce 看看即将发布的 Spring 3.1.0 版本中的新功能(参见我的上一次编辑)。
【解决方案2】:

我会选择这个版本:

ContextConfiguration(locations = { "/my_spring_test.xml" })
public abstract class AbstractMyTestCase extends AbstractJUnit4SpringContextTests

my_spring_test.xml 中,我会使用PropertyPlaceHolderConfigurer 机制。

JPA 示例:

<context:property-placeholder
    system-properties-mode="OVERRIDE" 
    location="classpath:test.properties" />

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

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="test" />
    <property name="dataSource" ref="dataSource" />
    <property name="persistenceXmlLocation"
             value="classpath:META-INF/persistence.xml" />
    <property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    <property name="showSql" value="false" />
    <property name="generateDdl" value="${test.database.update}" />
    <property name="database" value="${test.database.databasetype}" />
</bean>
    </property>
</bean>

现在您需要做的就是在类路径上为内存测试和真正的集成测试设置不同版本的 test.properties(当然,相应的驱动程序类也需要存在)。您甚至可以设置系统属性来覆盖属性值。


如果你想用 maven 准备这个,你会发现用 maven 复制文件并非易事。您将需要一种执行代码的方法,标准选择是maven-antrun-plugingmaven-maven-plugin

无论哪种方式:有两个配置文件,例如在 src/main/config 中添加两个插件执行,一个在阶段generate-test-resources 中,一个在阶段pre-integration-test 中。这是 GMaven 版本:

<plugin>
    <groupId>org.codehaus.gmaven</groupId>
    <artifactId>gmaven-plugin</artifactId>
    <version>1.3</version>
    <executions>
        <execution>
            <phase>pre-integration-test</phase>
            <goals>
                <goal>execute</goal>
            </goals>
            <configuration>
            <source>
            new File(
               pom.build.testOutputDirectory,
               "test.properties"
            ).text = new File(
                       pom.basedir,
                       "src/main/config/int-test.properties"
            ).text;
            </source>
            </configuration>
        </execution>
        <execution>
            <phase>generate-test-resources</phase>
            <goals>
                <goal>execute</goal>
            </goals>
            <configuration>
            <source>
            new File(
               pom.build.testOutputDirectory,
               "test.properties"
            ).text = new File(
                       pom.basedir,
                       "src/main/config/memory-test.properties"
            ).text;
            </source>
            </configuration>
        </execution>
    </executions>
</plugin>

【讨论】:

  • 是的,我已经在使用属性占位符并考虑替换类路径上的文件。使用 Maven 最简单的方法是什么?分离仅包含单个属性文件的项目/依赖项,然后以某种方式根据阶段/配置文件调整依赖项?
  • 谢谢,虽然我现在不想在项目中介绍 Groovy。我认为问题在于 Maven 缺乏适当的集成测试支持:willcode4beer.com/opinion.jsp?set=maven2_integration-test 我会检查是否可以使用 mave-antrun-pluging 甚至使用 Maven 资源插件(资源:复制资源)
  • 您并没有在项目中引入 groovy,只是在构建中添加了两行 groovy。除了构建之外,您的项目仍然没有对 Groovy 的依赖(不需要部署 jar)。与 antrun 相同,您的项目将不依赖于 ant。
  • 嗯,看来这两个答案都是可行的,但只能选择一个作为正确答案...?
  • @Puce 选择或投掷硬币 :-)
【解决方案3】:

我没有成功使用 Spring 3.x context:property-placeholder 标记。我使用了老式 bean 标记和属性文件,并且能够在我的代码和数据库之间建立连接,如下所示:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="/com/my/package/database.properties"/>
</bean>


<bean id="myDatasource" class="oracle.ucp.jdbc.PoolDataSourceFactory" 
    factory-method="getPoolDataSource">
    <property name="URL" value="${JDBC_URL}"/>
    <property name="user" value="${JDBC_USERNAME}"/>
    <property name="password" value="${JDBC_PASSWORD}"/>
    <property name="connectionFactoryClassName"   
      value="oracle.jdbc.pool.OracleConnectionPoolDataSource"/>
    <property name="ConnectionPoolName" value="SCDB_POOL"/>
    <property name="MinPoolSize" value="5"/>
    <property name="MaxPoolSize" value="50"/>
    <property name="connectionWaitTimeout" value="30"/>
    <property name="maxStatements" value="100"/>
</bean>

以下是属性文件的示例:

JDBC_URL=jdbc:oracle:thin:@myDB:1521:mySchema
JDBC_USERNAME=username
JDBC_PASSWORD=password

然后我像这样设置我的 JUnit 测试:

@ContextConfiguration(locations = {"/com/my/pkg/test-system-context.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class HeaderDaoTest {

    @Autowired
    HeaderDao headerDao;

    @Test
    public void validateHeaderId() {
        int headerId = 0;

        headerId = headerDao.getHeaderId();

        assertNotSame(0,headerId);
    }

}

这对我有用,但每个人做事的方式都有些不同。希望这会有所帮助。

【讨论】:

    【解决方案4】:

    我最近遇到了同样的问题,在查看 documentation for the @ContextConfiguration annotation 时,我注意到了 inheritLocations 选项。

    通过将其添加到我的班级,例如

    @ContextConfiguration(locations = { "/my_spring_integration_test.xml" }, inheritLocations=false)
    public class FooIntegrationTest extends FooTest
    

    我发现我能够根据需要覆盖 ContextConfiguration。

    【讨论】:

      猜你喜欢
      • 2010-09-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-26
      • 1970-01-01
      • 1970-01-01
      • 2019-07-06
      • 1970-01-01
      相关资源
      最近更新 更多