How to reuse Spring context when running Ant junit batchtest - spring

I'm in the process of upgrading our application to JUnit4. I've managed to get our test cases up and running using the Spring annotations
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
We then execute all tests with Ant using
<junit ...>
<batchtest fork="yes" todir="tmp">
<fileset dir="${testsrc.dir}">
<include name="**/Test*.java"/>
</fileset>
</batchtest>
However, based on our logfiles it appears that the Spring context is re-created for every single test class. Thus, total execution time is way too high. What is the proper approach to have the Spring context only loaded once?
Thanks
Simon Niederberger

Maybe it's because of the fork parameter? Seems like ant is creating a fork for each single test.
I guess normally springs junit runner tries to reuse the context.

Related

Structure test scripts with liquibase to execute in order with normal scripts

we are using liquibase for migrating database in spring boot application. In resources we have main changelog file which includes other changelogs (1 per version).
We usually differentiate environments by liquibase's context attribute but new we need differentiate data which are only for integration tests, and don't want place it next to normal versioned scripts. Is possible place these integration tests scripts in test's scope of project and execute them in order with normal scripts?
For instance:
main changelog:
<include file="version-1.xml"/>
<include file="version-2.xml"/>
and version 1 sample:
<changeSet id="1ver_1" author="xxx">
<!-- creation of table foo_table -->
</changeSet>
<changeSet id="1ver_2" author="xxx">
<!-- adding column to table foo_table -->
</changeSet>
version 2 sample:
<changeSet id="2ver_1" author="xxx">
<!-- renaming table foo_table to bar_table -->
</changeSet>
I need that if scripts for integration tests will written after script 1ver_1 and will contains inserts, it will be ok if next will be executed 1ver_2 and 2ver_1.
So when db for instegration tests started, scripts will be executed in right order:
1ver_1
test_data for 1ver_1
1ver_2
2ver_1
what is best practice to do that?
I think you should change the way you keep your changesets. Have a look at Liquibase Best Practices.
So your master changelog should look like:
<include file="version-1.1.xml"/>
<include file="version-1.2.xml"/>
<include file="version-2.1xml"/>
If you do this, you can have dedicated master changelog file for integration test. Your integration test's changelog-master.xml will look like this:
<include file="version-1.1.xml"/>
<include file="test_data_version-1.1.xml"/>
<include file="version-1.2.xml"/>
<include file="version-2.1xml"/>
After that you just override property in integration tests:
liquibase.change-log=classpath:integration-liquibase-changeLog.xml
Also you should place integration-liquibase-changeLog.xml and all 'test_data_xx.xml' into integration test module resource or test resource (it depends on project structure). The main idea it should not be provided to production artifacts.

Multiple Embedded HSQLDB databases in jUnit errors during build

I'm working on a new Spring Batch (3.0.3.RELEASE) application where there will be multiple databases accessed during the jobs. For testing we are using HSQLDB (2.3.2) as the embedded database.
In my Application context I have the following.
<jdbc:embedded-database id="dataSource">
</jdbc:embedded-database>
<jdbc:embedded-database id="proDataSource">
<jdbc:script location="classpath:script-tables.sql" />
<jdbc:script location="classpath:script-constraints.sql" />
</jdbc:embedded-database>
<jdbc:embedded-database id="altDataSource">
<jdbc:script location="classpath:script-alt-tables.sql" />
</jdbc:embedded-database>
When I run a single test in Eclipse, things are fine. When I build from the command line, after the first test, I get errors
Failed to execute SQL script statement at line 3 of resource class path resource [script-promrkt-promo.sql]
object name already exists: PROMRKT
It appears to me that the population process in EmbeddedDatabaseFactory is receiving an already populated database. From what I can tell is that after each test there is not a SHUTDOWN being executed and HSQLDB is leaving the already populated database in memory.
I have re-reviewed the documentation and in a Spring Doc this does show a explicit shutdown command. But if spring starts up the embedded database when my test starts why doesn't it shut it down when the test completes ?
Is it expected the embedded databases will remain after each unit test for the same application context?
What is the order that spring starts up an embedded database and when is the transactional context initialized?
Do I need to use a database cleaner ?
Can the populate be updated to only populate when the database is first started, and rollback to the original script configuration when my test is complete ( kinda like how the AbstractTransactionalSpringContextTests worked )
Do I need some transactional markers? Spring Batch's JobRepo is properly being populated and destroyed between each test. Why are my custom dataSources not ?
The script the log message is complaining about isn't in your configuration. I presume it's being executed somewhere else? If that's the case, you'll probably need to add #DirtiesContext to your tests so that Spring doesn't cache the context (I'm assuming you're using the SpringJunit4Runner with #ContextConfiguration but can't be sure since your actual test isn't in the question).
If my assumption is correct, Spring caches the context in an effort to improve performance over the running of a unit test suite. If your test modifies the context in a way that can impact other tests (like running scripts in one test that need to be run again in others), you mark the tests with #DirtiesContext and Spring won't cache the context. You can use the annotation at either the method or class level. You can read more about the annotation here: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/annotation/DirtiesContext.html
I spent a lot of time looking at this and reading the Spring Framework documentation (gasp!) and tracing through code. There are some interesting changes in 4.1 spring core, especially the testing.
I found out that ApplicationContext(s) are cached at the JVM level now. If a second test asks for a context the TestContext looks first in it's cache to see if some other test has already asked for the identical configuration.
I have some profiles for some of my tests. A test with a different profile but the same #ContextConfiguration causes the that context to be re-loaded with the profile applied. When the "Bean Loader" arrives at creating the embedded databases, the EmbeddedDatabaseFactory does not take into consideration that the embedded database (in memory HSQLDB) may have already been created or cached from previous tests and does not need to be re-initialized.
Therefore I added some logic to the EmbeddedDatabaseFactory.initDatabase() checking if the database already exists before re-initializing & running the DatabasePopulator.
List existingDataBases = org.hsqldb.DatabaseManager.getDatabaseURIs();
boolean isExisting = false;
String localDBName = StringUtils.lowerCase(this.databaseName);
for (Object object : existingDataBases) {
if (object.toString().contains(localDBName)) {
isExisting = true;
break;
}
}
// Now populate the database
if (!isExisting && this.databasePopulator != null) {
( of course this isn't quite kosher for what spring would need but it gets the point across )
In my opinion it looks like an issue partially with the EmbeddedDatabaseFactory and the TestContext caching mechanism. My "jdbc:embedded-database" definitions do not have any profiles associated with them. Why does the cache need to re-create them and not load them out of the existing cached beans?
You can try to force creation of new embedded database by setting unique name with generateUniqueName(true) each time new object is created.
Here is an example:
embeddedDatabase = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.generateUniqueName(true)
.addScripts("db/sql/create-db.sql", "db/sql/insert-data.sql")
.build();

How do I control spring injections that vary between the test environment and the production environment?

I'm setting up a CI situation in which I will deploy my web app to a test environment. In this test environment, I want the business objects used by the app to be mocks of the real ones; the mocks will return static test data. I'm using this to run tests agains my ui. I'm controlling the injections of these business object dependencies with Spring; it's a struts 2 application, for what that's worth.
My question is Maven related, I think. What is the best way to have my Maven build determine whether or not to build the spring configuration out for injecting the mocks or injecting the real thing? Is this a good use for maven profiles? Other alternatives?
Spring itself has support for profiles (if you're using 3.1 or newer), for a web-application you can use context-parameter to set the active profile for different environments in the web.xml:
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>test</param-value>
</context-param>
Edit: For Maven & Jenkins, you should be able to set the parameter for a build job as follows:
First, let Maven filter your xml-resources (in this example, only files ending with xml are filtered, others are included without filtering) by adding the following into your pom.xml inside the <build> </build> -tags:
<resources>
<resource>
<directory>src/main/webapp</directory>
<filtering>true</filtering>
<includes>
<include>**/*xml</include>
</includes>
</resource>
<resource>
<directory>src/main/webapp</directory>
<filtering>false</filtering>
<excludes>
<exclude>**/*xml</exclude>
</excludes>
</resource>
</resources>
Then, parameterize the context-param in your web.xml:
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>${env.SPRINGPROFILE}</param-value>
</context-param>
Then parameterize the build job in Jenkins to set the desired string parameter for SPRINGPROFILE (for example test or prod): https://wiki.jenkins-ci.org/display/JENKINS/Parameterized+Build
It's probably a bad idea to do anything with the build of the web app artifact ( Maven best practice for generating artifacts for multiple environments [prod, test, dev] with CI/Hudson support? ). While you could use various mechanisms to produce a WAR file with different configurations of the Spring injections for different contexts, the WAR artifact should be the same every time it's built.
In order to extract the configuration out of the WAR, I have used Spring 3's ability to pull in override values from an external property file. I define default, i.e. produciton, values of my business objects. And I configure spring to check for the existence of a properties file, something I will deploy when the app is in a testing environment and requires mock injections. If that properties file exists, it's values are injected instead. Here's the relevent bit of the spring config file.
<!-- These are the default values -->
<util:properties id="defaultBeanClasses">
<prop key="myManagerA">com.myco.ManagerAImpl</prop>
<prop key="myManagerB">com.myco.ManagerBImpl</prop>
</util:properties>
<!-- Pull in the mock overrides if they exist. -->
<context:property-placeholder
location="file:///my/location/mockBeans.properties"
ignore-resource-not-found="true"
properties-ref="defaultBeanClasses"/>
<!-- The beans themselves. -->
<bean id="managerA" class="${myManagerA}"/>
<bean id="managerB" class="${myManagerB}"/>
And here is the contents of the external "mockBeans.properties" file:
#Define mock implementations for core managers
myManagerA=com.myco.ManagerAMockImpl
myManagerB=com.myco.ManagerBMockImpl
This works nicely. You can even include the mockBeans.properties file in the actual WAR, if you like, but not in the live location. Then the test environment task would be too move it to the location pointed at by the spring config. Alternatively, you could have the mock properties reside in a completely different project.

How do I use Spring embedded database initialization scripts during the integration-test phase?

I can use Spring to create and initialize embedded databases either programmatically:
#Before
public void setUp() {
database = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql")
.addScript("init.sql")
.build();
....
}
or via Spring configuration:
<jdbc:initialize-database data-source="dataSource">
<jdbc:script location="classpath:schema.sql"/>
<jdbc:script location="classpath:init.sql"/>
</jdbc:initialize-database>
Here my scripts schema.sql and init.sql are stored in the directory src/test/resources. So if tests using the embedded databse are run with:
mvn test
then the files, being in src/test/resources are available, and everything is fine.
But now suppose I want to instead use the embedded database together with an embedded web server (Jetty or embedded Tomcat) in an integration test, run via
mvn integration-test
Now in this late phase, I want to do an almost end-to-end test that hitting certain urls for the web service return the expected HTTP responses, assuming certain data in the embedded database. But at this point, the war deployed in the embedded webserver does not have files from src/test/resources so my initialization scripts are not present, and I get the error
Caused by: java.io.FileNotFoundException: class path resource [schema.sql] cannot be opened because it does not exist
Now everything will work if I put the scripts in src/main/resources but these scripts are only for filling up an embedded database for testing and they do not belong in the war file at all. Does anyone know how to set up an integration test so that an emedded database can be used, without polluting the actual war file that will be deployed?
I was hoping that Spring's embedded database initialization could use something other than a file. I thought about JNDI but that would seem to require polluting the web.xml file with a resource definition for test data which would (again) appear in the deployable war. Or perhaps there are options using cargo? Some programmatic tricks with Spring that I don't know?
You should be able to do this:
<jdbc:initialize-database data-source="dataSource">
<jdbc:script location="file:///${project.basedir}/src/test/resources/schema.sql"/>
<jdbc:script location="file:///${project.basedir}/src/test/resources/init.sql"/>
</jdbc:initialize-database>

Loading a partial Spring context

I am not much of a Spring expert, but was given a legacy system with a huge context file (not separated into modules).
I want to add some unit tests - that validates different parts of the system, with the actual production configuration.
I started using the ClassPathXmlApplicationContext/FileSystemXmlApplicationContext classes to load the context, however - that takes forever.
Is it possible to load only parts of the context file (recursively) without the need to separate the original file into modules?
Update:
I'll just post here my implementation of Ralph's solution using maven:
my pom.xml:
<plugin>
<groupId>com.google.code.maven-config-processor-plugin</groupId>
<artifactId>maven-config-processor-plugin</artifactId>
<version>2.0</version>
<configuration>
<namespaceContexts>
<s>http://www.springframework.org/schema/beans</s>
</namespaceContexts>
<transformations>
<transformation>
<input>context.xml</input>
<output>context-test.xml</output>
<config>test-context-transformation.xml</config>
</transformation>
</transformations>
</configuration>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<phase>test</phase>
</execution>
</executions>
</plugin>
my test-context-transformation.xml:
<processor>
<add>
<name>/s:beans</name>
<value>
<![CDATA[
default-lazy-init="true"
]]>
</value>
</add>
</processor>
If you are trying to run "unit" tests, you will not require the full application context at all. Just instantiate the class you want to test (and maybe its collaborators, though mocking may be a better option) and off you go. Unit tests should concentrate on single components in isolation - otherwise they are not unit tests.
If you are trying to run a full integration test by creating the complete object hierarchy defined in your application context, then it may be easiest by first refactoring your context and splitting it into modules - as you were suggesting already.
I guess it does not work out of the box. But you can try this (it is just an idea, I don't know if it works)
Spring support so called lazy initialization the idea is to add this to all the beans.
I can imagine two ways.
A simple tool that create an copy of the orignal configuration xml file and add default-lazy-init="true" the container level beans (with s) declaration.
Try do do it programmatic. With a Bean Post Processor, or try to "inject" the default-lazy-init="true" configuration programmatic

Resources