Spring:单元和集成测试

时间:2011-01-04 12:25:31

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

我正在寻找使用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“})”重复了很多。

有什么建议吗?

此致 弗洛里安

4 个答案:

答案 0 :(得分:4)

我扩展了GenericXmlContextLoader

public class MyContextLoader extends GenericXmlContextLoader {

并覆盖了

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

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

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

@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 ,并且可以为不同的环境/配置文件选择配置文件。

答案 1 :(得分: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>

答案 2 :(得分:0)

我没有成功使用Spring 3.x上下文: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);
    }

}

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

答案 3 :(得分:0)

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

将此添加到我的班级,例如

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

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