Spring + Maven: separate property files for unit tests and integration tests - java

I'm using Spring 2.5.6 and building my project with Maven 2.2.1. We use PropertyPlaceholderConfigurer beans in Spring to load up properties for configuring things like the database. Pretty standard stuff. We also have two different sets of tests: unit tests and integration tests.
I would like to be able to use different property files to configure things like database url differently for the two different types of tests. For example, I want unit tests to use the localhost database and integration tests to use the mydatabase.example.com database.
I have tried several variations where I place the property files in separate subdirectories (one for unit tests and one for integration tests). From there, I've tried things like using the additionalClasspathElements tag for the maven-failsafe-plugin, but that didn't seem to work at all. I tried using the maven-antrun-plugin to copy the files into target/classes, but that didn't get triggered when I ran mvn verify -Dtest=sometest.
I also tried using systemPropertyVariables in maven to set a property called buildEnvironment, which I then tried to reference in my Spring bean definition:
<property name="locations">
<value>classpath:${buildEnvironment}/my-test.properties</value>
</property>
But Spring refused to resolve ${buildEnvironment}. At this point I'm out of ideas. I'm sure there's a nice, straightforward way to do this, but I can't figure it out.
Any advice would be greatly appreciated.

You could enable resource filtering and create maven properties:
<build>
<resources>
<resource>
<directory>src/test/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<properties>
<buildEnvironment>yourValue</buildEnvironment>
</properties>
${buildEnvironment} in your Spring config will then be resolved to "yourValue" (assuming it is under src/test/resources/)

Related

Read POM values in external application properties

I have the following properties in the pom file
<name>DemoApplication</name>
<description>Demo spring project</description>
<version>1.0.0-SNAPSHOT</version>
And I have a class that reads the properties from application.yml
But instead of using the application.yml under src/main/resources I am specifying the properties through an external file as follows
java -jar DemoApplication-1.0.0-SNAPSHOT.jar --spring.config.location=application.yml
In this external application properties, I have the following attributes
swagger:
apiTitle: '#project.name#'
apiDescription: '#project.description#'
apiVersion: '#project.version#'
The issue is that the #project.name# and other properties are not being replaced as expected, but are read as-is.
How should the problem be approached?
According that section of the official documentation of Spring Boot v2, you can configure it with :
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<configuration>
<delimiters>
<delimiter>#</delimiter>
</delimiters>
<useDefaultDelimiters>false</useDefaultDelimiters>
</configuration>
</plugin>
With useDefaultDelimiters set to false or to true depending on your configuration.
The others sections of that official documentation will be helpful for your use case, especially these one : "77.5 Use YAML for External Properties".
If nothing is working, why don't you are loading a custom Properties file ? It could be loaded as you need without any problem. Just reference it with the correct path when you are starting your program, and inside your program, test if your file config.properties is available and contains what you need to work with.
Of course, the Maven way of loading resources files is the best easy way to go, and it should be a simple Properties file too. I have done exactly that way inside the software I am released to manage my configuration :
Writing a app.properties
Loading that file with Maven at runtime with resource configuration
Expanding properties with classical syntax ${my.prop}
Run the program with a Maven task.
Of course, when you distribute your app as a jar, it is a bit different.
Maybe you can try to write your properties files within a Maven goal.

SpringBoot: How to make profile specific resources

I have profiles: dev, prod.
And my homepage located at /src/main/resources/static/index.html
How to make different homepage with different profile?
For example, /src/main/resources/static-dev/index.html and /src/main/resources/static-prod/index.html.
Any advice?
Finally I got a simple solution.
Use different config file application.properties and application-prod.properties.
Each of them I config a different resource location. For example spring.resources.static-locations=classpath:/static-dev/.
If your project supports the Maven dependency manager, Maven's build profiles may be able to help you:
<profile>
<id>live</id>
<properties>
<environment>live</environment>
</properties>
<build>
<resources>
<resource>
<directory>src/main/resources/${environment}</directory>
</resource>
</resources>
</build>
</profile>
The code above should be in your pom.xml. In your Spring properties you can specify the active profile in one line:
spring.profiles.active=live
This should be enough to conditionally load any resources.
Both resources should be put under /src/main/resources/static (since this is the default static resource folder IIRC) and then categorized into /prod and /dev. Then in your #GetMapping controller, choose to return /prod/index or /dev/index based on your condition
You can create a Filter that changes the request URL from /index.html to /dev/index.html or /prod/index.html as needed.
The filter can also do the /dev or /prod prefixing for .css and .js files.
Unless all your files are split between dev and prod, you'd probably need an explicit list of which requests should be prefixed.

How to dynamically change values in weblogic.xml?

I'd like to use values from a properties file (or some other filesystem resource) in my weblogic.xml. For example, I have this section:
<session-descriptor>
<cookie-name>JSESSIONID</cookie-name>
<cookie-domain>${my.domain}</cookie-domain>
</session-descriptor>
I then have a properties file specifying the value:
my.domain=qa.mydomain.com
on the file system specifying the domain.
Is this possible? Many other configuration mechanisms allow for this. The motivation is that the same code could be deployed in multiple environments with multiple domains and weblogic could simply take the appropriate domain from the file without any operator intervention.
Running weblogic 12c here.
Thanks!
This can be simply achieved using maven's resource plugin, assuming you already on mvn build.
You just need to add below configuration under <build> section
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
You can add below property in your main pom in respective profiles:
<my.domain>desiredValue</my.domain>
In my opinion what you need is to use Weblogic's Deployment Plan feature.
I'm not so familar with it (never used it in productive environments) but with a deployment plan you should be able to change values in web.xml/weblogic.xml during deployment time.
Docs/Example:
Oracle Help Center - Creating and Using a Deployment Plan
Oracle Docs - Save Deployment Plan
Example from middlewaremagic.com

Why does Maven test not work after mvn clean unless I run mvn update?

I'm running some service testing using restassured and cucumber and they work fine locally just using Maven test.
The issue is if I run Maven clean, then I must run Maven update or it will not work (Says it can't find my Cucumber feature files). For reference it says:
No features found at [classpath:classpath/classpath]
This wouldn't be a huge issue except I need to have this running through Bamboo where I can't call Maven update.
So I either need to figure out what is wrong with my POM to begin with to cause this issue, or how I can run Maven update through the goals/environment variables.
The POM is fairly simple, only having the needed dependencies/reporting stuff.
The build part of the POM is as follows:
<build>
<finalName>Test</finalName>
<directory>target</directory>
<outputDirectory>target/classes</outputDirectory>
<testOutputDirectory>target/test-classes</testOutputDirectory>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<resources>
<resource>
<directory>src/test/resources</directory>
</resource>
</resources>
</build>
This is all in Java 8 using Eclipse as the IDE.
I would avoid specifying anything in the build section in my pom and instead use the default values.
That is, I would keep my feature files in the same package as the runner or a sub package.
The runner could for example live in the package se/thinkcode/tage
As in the directory:
./test/java/se/thinkcode/tage
This means that the feature files should live in the directory:
./test/resources/se/thinkcode/tage
This would allow me to minimize the configuration in the runner. I typically use runners that looks like this:
package se.thinkcode.tage;
import cucumber.api.junit.Cucumber;
import org.junit.runner.RunWith;
#RunWith(Cucumber.class)
public class RunCukesTest {
}
This is the smallest configuration possible if you want to run Cucumber using JUnit from Maven.
It is even smaller that the example supplied by the Cucumber team: https://github.com/cucumber/cucumber-java-skeleton
Looks like defining the features/glue in the cucumber options fixed this.
I do believe there is a better option though.
I added the following cucumber options:
features ="src/test/java",
glue = "packagename",

Spring integration tests with profile

In our Spring web applications, we use the Spring bean profiles to differentiate three scenarios: development, integration, and production. We use them to connect to different databases or set other constants.
Using Spring bean profiles works very well for the changing the web app environment.
The problem we have is when our integration test code needs change for the environment. In these cases, the integration test loads the application context of the web app. This way we don't have to redefine database connections, constants, etc. (applying the DRY principle).
We setup our integration tests like the following.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = ["classpath:applicationContext.xml"])
public class MyTestIT
{
#Autowired
#Qualifier("myRemoteURL") // a value from the web-app's applicationContext.xml
private String remoteURL;
...
}
I can make it run locally using #ActiveProfiles, but this is hard-coded and causes our tests to fail on the build server.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = ["classpath:applicationContext.xml"])
#ActiveProfiles("development")
public class MyTestIT
{ ... }
I also tried using the #WebAppConfiguration hoping that it might somehow import the spring.profiles.active property from Maven, but that does not work.
One other note, we also need to configure our code so that developers can run the web app and then run the tests using IntelliJ's test runner (or another IDE). This is much easier for debugging integration tests.
As other people have already pointed out, you can opt to use Maven to set the spring.profiles.active system property, making sure not to use #ActiveProfiles, but that's not convenient for tests run within the IDE.
For a programmatic means to set the active profiles, you have a few options.
Spring 3.1: write a custom ContextLoader that prepares the context by setting active profiles in the context's Environment.
Spring 3.2: a custom ContextLoader remains an option, but a better choice is to implement an ApplicationContextInitializer and configure it via the initializers attribute of #ContextConfiguration. Your custom initializer can configure the Environment by programmatically setting the active profiles.
Spring 4.0: the aforementioned options still exist; however, as of Spring Framework 4.0 there is a new dedicated ActiveProfilesResolver API exactly for this purpose: to programmatically determine the set of active profiles to use in a test. An ActiveProfilesResolver can be registered via the resolver attribute of #ActiveProfiles.
Regards,
Sam (author of the Spring TestContext Framework)
I had a similar problem: I wanted to run all of my integration tests with a default profile, but allow a user to override with a profile that represented a different environment or even db flavor without having to change the #ActiveProfiles value. This is doable if you are using Spring 4.1+ with a custom ActiveProfilesResolver.
This example resolver looks for a System Property, spring.profiles.active, and if it does not exist it will delegate to the default resolver which simply uses the #ActiveProfiles annotation.
public class SystemPropertyActiveProfileResolver implements ActiveProfilesResolver {
private final DefaultActiveProfilesResolver defaultActiveProfilesResolver = new DefaultActiveProfilesResolver();
#Override
public String[] resolve(Class<?> testClass) {
if(System.getProperties().containsKey("spring.profiles.active")) {
final String profiles = System.getProperty("spring.profiles.active");
return profiles.split("\\s*,\\s*");
} else {
return defaultActiveProfilesResolver.resolve(testClass);
}
}
}
And in your test classes, you would use it like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles( profiles={"h2","xyz"},
resolver=SystemPropertyActiveProfileResolver.class)
public class MyTest { }
You can of course use other methods besides checking for the existence of a System Property to set the active profiles. Hope this helps somebody.
If you want to avoid hard-coding the profile you may want to use the system property spring.profiles.active and set it to whatever you need in that particular environment e.g. we have "dev", "stage" and "prod" profiles for our different environments; also we have a "test", "test-local" and "test-server" profiles for our testing.
Remember that you can have more than one profile in that system property by using a list of comma-separated values e.g. "test,test-qa".
You can specify system properties in a maven project in the maven surefire plugin or passing them like this:
mvn -DargLine="-DpropertyName=propertyValue"
As #ElderMael mentioned you could use the argLine property of maven surefire plugin. Often when I need to run all the test with different specific Spring profiles I define additional maven profile. Example:
<profiles>
<profile>
<id>foo</id>
<dependencies>
<!-- additional dependencies if needed, i.e. database drivers ->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Dspring.profiles.active=foo</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
With that approach you could easily run all the test with activated profile by maven command:
mvn clean test -Pfoo
The #ActiveProfile annotation is good but sometimes we need to run all the test with activated specific profiles and with hard-coded #ActiveProfile parameters it is a problem.
For example: by default integration test with H2 in-memory db, but sometimes you want to run test on the "real" database. You could define that additional maven profile and define Jenkins job. With SpringBoot you could also put additional properties to test/resources with name application-foo.yml (or properties) and those properties will be taken into account to.
there are many faces to this problem.
in my case, a simple addition to build.gradle already helped:
test { systemProperties = System.properties }

Categories

Resources