Spring: unit tests and integration

I am looking for best practices for setting up unit tests and integration using Spring.

I usually use 3 types of tests:

  • "real" unit tests (no dependencies) Tests
  • run either as a β€œsingle” test (in-memory db, local calls, mock objects, ...) or as an integration test (constant db, remote calls, ...)
  • tests are performed only as integration tests

Currently, I only have tests of the second category, which is the difficult part. I set the base test class, for example:

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

And unit tests, such as:

 public class FooTest extends AbstractMyTestCase 

with attributes with auto-substitution.

What is the best way to run a test in a different (integration test) environment? Subclass test and override ContextConfiguration?

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

Will this work (I can’t easily test it now)? The problem with this approach is that "@ContextConfiguration (locations = {" / my _spring_integration_test.xml "})" is duplicated a lot.

Any suggestions?

Regards, Florian

+8
java spring unit-testing maven-2 integration-testing
source share
4 answers

I have expanded GenericXmlContextLoader

public class MyContextLoader extends GenericXmlContextLoader {

and overrote

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

a method for collecting the names of the directory configuration files, which I can specify using SystemProperty (-Dtest.config =).

I also changed the following method to NOT change any locations.

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

I use this context loader as follows

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

Running a test using SystemProperty, indicating the source of the configuration files, now allows you to use completely different configurations.

Using SystemProperty is, of course, only one strategy for specifying a configuration location. You can do whatever you want in generateDefaultLocations() .


EDIT:

This solution allows you to use complete various configurations of the application context (for example, for layouts), and not just different properties. You do not need a build step to deploy everything in your "classpath" location. My particular implementation also used the default username to search for the configuration directory (src / test / resources / {user}) if the system property was not set (makes it easier to support specific test environments for all project developers).

Using the PropertyPlaceholder is still possible and recommended.


EDIT

Spring Version 3.1.0 will support XML profiles / environments An abstraction that is similar to my solution and will allow you to select configuration files for different environments / profiles.

+4
source share

I would go with this version:

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

and in my_spring_test.xml , I would 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 you need to have different versions of test.properties on the class path for built-in and real integration tests (and, of course, the corresponding driver classes must be present). You can even set system properties to overwrite property values.


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

In any case: two configuration files, for example. in src / main / config and add two executions of the plugin, one in the generate-test-resources phase and one in the pre-integration-test phase. Here is 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> 
+2
source share

I have not been successful in using Spring 3.x context: tag-tag-property-placeholder. I used the old bean fashion tag along with the properties file and was able to establish a connection between my code and my database as follows:

 <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 is an example properties file:

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

Then I installed my JUnit test as follows:

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

It worked for me, but everyone does it differently. Hope this helps.

0
source share

I recently ran into the same problem and looked through the documentation for the @ContextConfiguration annotation , I noticed the inheritLocations option.

By adding this to my class, for example.

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

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

0
source share

Source: https://habr.com/ru/post/650665/


All Articles