ChatGPT解决这个技术问题 Extra ChatGPT

Spring-boot default profile for integration tests

Spring-boot utilizes Spring profiles 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:

Create application-test.properties inside src/main/resources Write test specific config there (just the database name for now) 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


C
Community

As far as I know there is nothing directly addressing your request - but I can suggest a proposal that could help:

You could use your own test annotation that is a meta annotation comprising @SpringBootTest and @ActiveProfiles("test"). So you still need the dedicated profile but avoid scattering the profile definition across all your test.

This annotation will default to the profile test and you can override the profile using the meta annotation.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@SpringBootTest
@ActiveProfiles
public @interface MyApplicationTest {
  @AliasFor(annotation = ActiveProfiles.class, attribute = "profiles") String[] activeProfiles() default {"test"};
}

How does one use this to declare multiple active profiles to be used by the annotation?
A
AmirHd

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


can we have an interface instead of a class here?
C
Compito

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.


I use this entry in my testcases if I want to avoid to set @ActiveProfiles("test"). Does it not work for you?
If I create src/test/resources/application.properties file, src/main/resources/application.properties content is ignored when running tests.
@ciastek You can add application-test.properties for tests and override only the properties you need.
@Advicer which doesn't get picked up unless the default properties specify spring.profiles.active=test like the answer says.
@OrangeDog exactly - maybe you can make use of the profile 'default' which is active by default. So you could add such a line in test/resources/application-default.properties (unless of course you already have a src/main/application-default.properties file :-)
a
aSemy

There are two approaches.

Load from config/

(2022 update, tested against Spring Boot 2.6)

Along with the approach below, you can also add config to src/test/resources/config/application.yml

src/
├── main/
│   ├── java/
│   │   └── ...
│   └── resources/
│       └── application.yml <- default properties, always loaded
└── test/
    ├── java/
    │   └── ...
    └── resources/
        └── config/
            └── application.yml <- test properties, will override the defaults

https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config.files

Spring Boot will automatically find and load application.properties and application.yaml files from the following locations when your application starts: From the classpath The classpath root The classpath /config package From the current directory The current directory The /config subdirectory in the current directory Immediate child directories of the /config subdirectory The list is ordered by precedence (with values from lower items overriding earlier ones). Documents from the loaded files are added as PropertySources to the Spring Environment.

Manual import using spring.config.import

(original answer from 2021, tested against Spring Boot 2.4)

One solution is to have 3 properties files and to import

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>

Note: if src/main/resources/application.yml contains spring.profiles.active: "dev", then this won't be overwritten by src/test/resources/application-test.yml


It's strange that without the src/test/resources/application.yml file, environment.getActiveProfiles() will still return the correct test profile but if I get the active profile via @Value("${spring.profiles.active:}") annotation, it will be null.
J
JohnW

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

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


Does it mean the default application.properties in the classpath gets parsed too, then the test/resources/application-default.properties and then, because the profile "test" is detected, the test/resources/application-test.properties gets parsed? Otherwise it would not solve @ciastek's issue as commented under @Compito's answer.
I am getting this error with Spring Boot 2.4.5: "Property 'spring.profiles.active' imported from location 'class path resource [application-default.yml]' is invalid in a profile specific resource [origin: class path resource [application-default.yml]"
M
Matze

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.


Many good ideas here useful for many cases. IMHO @Matze answer is the most concise and straightforward answer for this question, No Profiles Needed, No Modifying Test Code Needed... Also logging is cleaner (so confusing in my case that Spring logs Using dialect: org.hibernate.dialect.PostgreSQL93Dialect when my test is, thankfully, using the test H2 database instead).
Wow, I did not expect to find THE right answer so deep in the list.
B
Bojan Vukasovic

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.


Worked for me, but had to add configurations to surefire plugin too along with failsafe.
D
Demel

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

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

E
Eduardo

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


You should use @ActiveProfiles, not @TestPropertySource.
I think it doesn't mind for using @TestPropertiesSource. It's also the way to load the configuration between the profile test configuration.
V
Valtoni Boaventura

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.


R
Rajan Mishra

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


G
Grigory Kislin

I've usually done a base class for all integration tests with common code and annotations. Do not forget make it abstract in order not to instatiate. E.g:

@SpringBootTest
@Transactional
@AutoConfigureMockMvc
@ActiveProfiles("test")
public abstract class AbstractControllerTest {

    @Autowired
    protected MockMvc mockMvc;

    protected ResultActions perform(MockHttpServletRequestBuilder builder) throws Exception {
        return mockMvc.perform(builder);
    }
}

// All annotations are inherited
class AccountControllerTest extends AbstractControllerTest {
....

M
Michael Knopf

The best solution I have found is the last suggestion here: https://inspeerity.com/blog/setting-default-spring-profile-for-tests-with-override-option/ The author also desribes the problem very clearly and discusses the downside of every other approach I can think of.

Create a file application-default.properties in your test resources, containing a single line:

spring.profiles.active=test

This takes advantage of the fact that Spring automatically enables a "default" profile if no other profiles were explicitly set. Now, your application-test.properties file will be used by default, for all tests.


Actually, looks like this is no longer supported as of Spring Boot 2.4... stackoverflow.com/questions/67935961/…
B
Bishal Jaiswal

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