[java] Spring-boot default profile for integration tests

Spring-boot utilizes Spring profiles (http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html) which allow for instance to have separate config for different environments. One way I use this feature is to configure test database to be used by integration tests. I wonder however is it necessary to create my own profile 'test' and explicitly activate this profile in each test file? Right now I do it in the following way:

  1. Create application-test.properties inside src/main/resources
  2. Write test specific config there (just the database name for now)
  3. In every test file include:

    @ActiveProfiles("test")
    

Is there a smarter / more concise way? For instance a default test profile?

Edit 1: This question pertains to Spring-Boot 1.4.1

This question is related to java spring spring-boot

The answer is


In my case I have different application.properties depending on the environment, something like:

application.properties (base file)
application-dev.properties
application-qa.properties
application-prod.properties

and application.properties contains a property spring.profiles.active to pick the proper file.

For my integration tests, I created a new application-test.properties file inside test/resources and with the @TestPropertySource({ "/application-test.properties" }) annotation this is the file who is in charge of picking the application.properties I want depending on my needs for those tests


If you use maven, you can add this in pom.xml:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-failsafe-plugin</artifactId>
            <configuration>
                <argLine>-Dspring.profiles.active=test</argLine>
            </configuration>
        </plugin>
        ...

Then, maven should run your integration tests (*IT.java) using this arugument, and also IntelliJ will start with this profile activated - so you can then specify all properties inside

application-test.yml

and you should not need "-default" properties.


You could put an application.properties file in your test/resources folder. There you set

spring.profiles.active=test

This is kind of a default test profile while running tests.


Another way to do this is to define a base (abstract) test class that your actual test classes will extend :

@RunWith(SpringRunner.class)
@SpringBootTest()
@ActiveProfiles("staging")
public abstract class BaseIntegrationTest {
}

Concrete test :

public class SampleSearchServiceTest extends BaseIntegrationTest{

    @Inject
    private SampleSearchService service;

    @Test
    public void shouldInjectService(){
        assertThat(this.service).isNotNull();
    }
} 

This allows you to extract more than just the @ActiveProfiles annotation. You could also imagine more specialised base classes for different kinds of integration tests, e.g. data access layer vs service layer, or for functional specialties (common @Before or @After methods etc).


Come 2021 and Spring Boot 2.4 the solution I have found is to have 3 properties files

  • src/main/resources/application.yml - contains the application's default props
  • src/test/resources/application.yml - sets the profile to 'test', and imports properties from 'main'
  • src/test/resources/application-test.yml - contains test-specific profiles, which will override 'main'

Here is the content of src/test/resources/application.yml:

# for testing, set default profile to 'test'
spring.profiles.active: "test"
# and import the 'main' properties
spring.config.import: file:src/main/resources/application.yml

For example, if src/main/resources/application.yml has the content

ip-address: "10.7.0.1"
username: admin

and src/test/resources/application-test.yml has

ip-address: "999.999.999.999"
run-integration-test: true

Then (assuming there are no other profiles)...

when running tests,

profiles=test
--
ip-address=999.999.999.999
username=admin
run-integration-test=true

and when running the application normally

profiles=none
--
ip-address=10.7.0.1
username=admin
run-integration-test <undefined>

You can put your test specific properties into src/test/resources/config/application.properties.

The properties defined in this file will override those defined in src/main/resources/application.properties during testing.

For more information on why this works have a look at Spring Boots docs.


Another programatically way to do that:

  import static org.springframework.core.env.AbstractEnvironment.DEFAULT_PROFILES_PROPERTY_NAME;

  @BeforeClass
  public static void setupTest() {
    System.setProperty(DEFAULT_PROFILES_PROPERTY_NAME, "test");
  }

It works great.


A delarative way to do that (In fact, a minor tweek to @Compito's original answer):

  1. Set spring.profiles.active=test in test/resources/application-default.properties.
  2. Add test/resources/application-test.properties for tests and override only the properties you need.

To activate "test" profile write in your build.gradle:

    test.doFirst {
        systemProperty 'spring.profiles.active', 'test'
        activeProfiles = 'test'
    }

Add spring.profiles.active=tests in your application.properties file, you can add multiple properties file in your spring boot application like application-stage.properties, application-prod.properties, etc. And you can specify in your application.properties file while file to refer by adding spring.profiles.active=stage or spring.profiles.active=prod

you can also pass the profile at the time running the spring boot application by providing the command:

java -jar-Dspring.profiles.active=localbuild/libs/turtle-rnr-0.0.1-SNAPSHOT.jar

According to the profile name the properties file is picked up, in the above case passing profile local consider the application-local.properties file


If you simply want to set/use default profile at the time of making build through maven then, pass the argument -Dspring.profiles.active=test Just like

mvn clean install -Dspring.profiles.active=dev


Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to spring

Are all Spring Framework Java Configuration injection examples buggy? Two Page Login with Spring Security 3.2.x Access blocked by CORS policy: Response to preflight request doesn't pass access control check Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean Failed to auto-configure a DataSource: 'spring.datasource.url' is not specified Spring Data JPA findOne() change to Optional how to use this? After Spring Boot 2.0 migration: jdbcUrl is required with driverClassName The type WebMvcConfigurerAdapter is deprecated No converter found capable of converting from type to type

Examples related to spring-boot

Access blocked by CORS policy: Response to preflight request doesn't pass access control check Why am I getting Unknown error in line 1 of pom.xml? Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured How to resolve Unable to load authentication plugin 'caching_sha2_password' issue ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean Failed to auto-configure a DataSource: 'spring.datasource.url' is not specified After Spring Boot 2.0 migration: jdbcUrl is required with driverClassName ERROR Source option 1.5 is no longer supported. Use 1.6 or later How to start up spring-boot application via command line? JSON parse error: Can not construct instance of java.time.LocalDate: no String-argument constructor/factory method to deserialize from String value