Spring: unit and integration tests

前端 未结 4 481
北恋
北恋 2020-12-30 13:07

I\'m looking for best practices for setting up unit and integration tests using Spring.

I usually use 3 kind of tests:

  • \"real\" unit tests (no dependen
相关标签:
4条回答
  • 2020-12-30 13:17

    I extended the GenericXmlContextLoader

    public class MyContextLoader extends GenericXmlContextLoader {

    and overrote the

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

    method to collect the config file names of a directory which I can specify by a SystemProperty (-Dtest.config=).

    I also modified the follwowing method to NOT modify any locations

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

    I use this context loader like this

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

    Running the test with a SystemProperty indicating the source of the config files enables you now to use completely different configurations.

    The usage of a SystemProperty is of course only one strategy to specify the configuration location. You can do whatever you want in generateDefaultLocations().


    EDIT:

    This solution enables you to use complete different application context configurations (e.g. for mock objects) and not only different properties. You do not need a build step to deploy everything to your "classpath" location. My concrete implementation also used the users name as default to look for a configuration directory (src/test/resources/{user}) if no system property is given (makes it easy to maintain specific test environments for all developers on the project).

    The usage of the PropertyPlaceholder ist still possible and recommended.


    EDIT:

    Spring Version 3.1.0 will support XML profiles/Environment Abstraction which is similar to my solution and will enable the choice of configuration files for different environments/profiles.

    0 讨论(0)
  • 2020-12-30 13:18

    I'd go with this version:

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

    and in my_spring_test.xml, I'd use the PropertyPlaceHolderConfigurer mechanism.

    Example for 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>
    

    Now all you need to do is have different versions of test.properties on the class path for in-memory and real integration tests (and of course the respective driver classes need to be present). You can even set system properties to overwrite the property values.


    If you want to prepare this with maven, you will find that copying files with maven is not trivial. You will need a way to execute code, the standard choices being the maven-antrun-plugin and gmaven-maven-plugin.

    Either way: have two configuration files, e.g. in src/main/config and add two plugin executions, one in phase generate-test-resources and one in phase pre-integration-test. Here's the GMaven version:

    <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>
    
    0 讨论(0)
  • 2020-12-30 13:28

    I have had no success in using Spring 3.x context:property-placeholder tag. I have used the old fashion bean tag along with a properties file and was able to set up a connection between my code and my database like so:

    <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>
    

    Here's an example of the properties file:

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

    Then I set up my JUnit test like so:

    @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);
        }
    
    }
    

    That worked for me, but everybody does things a little differently. Hope this helps.

    0 讨论(0)
  • 2020-12-30 13:35

    I recently ran in to the same problem and looking at the documentation for the @ContextConfiguration annotation, I noticed the inheritLocations option.

    By adding this to my class e.g.

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

    I found that I was able to override the ContextConfiguration as desired.

    0 讨论(0)
提交回复
热议问题