Spring boot unit test how to load files - java

everyone。When I was writing junit, I found that #ActiveProfiles corresponds to my resource directory。
I don’t understand how spring boot loads resource files,And why if I don’t specify #ActiveProfiles which application file is read by default?
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#SpringBootTest(classes = Application.class)
#ActiveProfiles("test")
public class NewTutorGroupIaoTest {
}
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#SpringBootTest(classes = Application.class)
#ActiveProfiles("test")
public class NewTutorGroupIaoTest {
}
project directory:
project
- src
- main
- java
- resource
- test
- java
- resource

If you mark classes with #Profile("test"), you can ensure they're loaded (into the ApplicationContext) by activating the test profile - either with #ActiveProfiles("test"), spring.profiles.active=test, or a number of other ways. Classes can be excluded with #Profile("!test")
More details here: https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-profiles
Also, by activating a profile, you may also activate a properties file to be picked up... You may also have files in your project like:
application.properties
application-default.properties
application-test.properties
If you have application.properties present as well as application-test.properties, application.properties forms the base config, and application-test.properties will overwrite any existing properties and may also supply additional configuration values.
If you supply/specify no profile, the default profile is activated. This will result in application.properties + application-default.properties being combined (as before).
You will see in the logs which profile is activated very near the beginning of the Spring log output.

Related

Switch Spring annotations depends on active profile

I have the following configuration for Spock class which is responsible for running integration tests.
#ContextConfiguration(classes = AccountService.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#Sql(executionPhase= Sql.ExecutionPhase.BEFORE_TEST_METHOD,scripts="classpath:/integration-tests/clear-tables.sql")
#AutoConfigureEmbeddedDatabase
class AccountControllerIntegrationSpec extends Specification
{
.....
}
I have two profiles. The purpose is to active use set of annotations :
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#Sql(executionPhase= Sql.ExecutionPhase.BEFORE_TEST_METHOD,scripts="classpath:/integration-tests/clear-tables.sql")
when I run docker-test profile.
When I run profile test I would like to use only #AutoConfigureEmbeddedDatabase (postgress embedded database for tests).
Profile test-docker should use real database from other container.
Profile test should use embedded database Postgress when I run tests locally on machine.
How can I configure during startup spring to choose databaseSource ?
When I add both annontations each time Embedded Postgres is only active even if test-docker profile is active I tried to set scope of dependency for embedded Postgres set differently for profiles. But it did not help.

Stop Spring Boot from loading #Configuration file from dependency module (jar)

I'm implementing a Spring boot application with a dependency to another spring module (jar) containing an #Configuration (AmcConfiguration.class) file I do NOT want loaded into the context. I've tried many variations of exclude examples
a few of which are as follows:
//exclude problem configuration class
#SpringBootApplication(exclude={AmcConfiguration.class})
and...
//exclude problem bean from within problem configuration class
#EnableAutoConfiguration
#Configuration
#ComponentScan(excludeFilters = #Filter(type = FilterType.ASSIGNABLE_TYPE, classes = IAmsClient.class))
and...
//exclude problem configuration class package
#EnableAutoConfiguration
#Configuration
#ComponentScan(excludeFilters = #Filter(type = FilterType.REGEX, pattern="com.prot.mtrx.amc.config.*"))
Also, I've made sure there are no package naming collisions.
spring boot root = "com.prot.am.*
dependency = "com.prot.mtrx.amc.*"
I've been searching examples for a few days now and am running out of options. I went down the auto-configuration path but that seems much too complicated a solution to tell spring boot to not run one simple configuration class.
From my logs, it looks like it might be embedded tomcat:
DEBUG o.a.tomcat.util.digester.Digester - New match='mbeans-descriptors/mbean/operation/parameter'
DEBUG o.s.c.a.ClassPathBeanDefinitionScanner - Identified candidate component class: file [C:\TestSuite\workspace-dev\MAMClient\target\classes\com\prot\mtrx\amc\config\AmcConfiguration.class]
What am I missing?

Spring integration test module running app from separate module without properties for DB config

Given a spring boot gradle module named "md-core" with a Spring application runner and a PostgresDbConfig to connect it to the DB.
The app runs perfectly fine and resolves the properties from the "application.yml" file in the #Config.
#Value("${db.default.pool.size}")
Integer maxPoolSize;
Now, a separate module called "integrationtests" tries to launch the Spring Boot Runner in the "md-core" module.
#ContextConfiguration(classes = {MdCore.class})
#RunWith(SpringJUnit4ClassRunner.class)
public class GivenTest
The test launches the "md-core" Spring runner, but when trying to resolve the properties in the #Config, it does not find any properties.
I've tried directly resolving the "application.yml" in the test. It does not send the properties over to the "md-core" module. Any attempt of adding the "application.yml" properties in the test resolves them to the test file, but does not send them over when the "md-core" module is accessed.
Are there any definitions which should be given in the test via annotations or in the gradle build files?
The classpath when launching the test does not contain the "md-core" resources location, only the classes.
Could this be a reason? If yes, how can the resources be referenced in the classpath of the gradle build files?
I apologize for any mistakes or incomplete information, this post is being written at the end of a work day, hoping there will be answers by morning.
Given this situation, the solution to use the application.properties file for the integration test is the simple addition of initializers = ConfigFileApplicationContextInitializer.class in the #ContextConfiguration annotation of the Test class:
#ContextConfiguration(
initializers = ConfigFileApplicationContextInitializer.class,
classes = {SomeApp.class})
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
public class SomeIntegrationTest {
#Test
public void testTheIntegration() {
// some integration tests here
}
}
Answer also documented in a post on my blog and initially found less detailed on another stackoverflow question.

#PropertySource on a spring configuration file

I am trying to write unit tests using Spring.
All the unit tests use the same spring configuration file, but each one has a different properties file with the same keys but different values to this unique test.
I want to define the properties file at the unit test class, but then it doesn't recognize the properties. The only way is to define it at the Spring configuration class, what means that I must change this configuration file every time I want to run a different test.
My test file:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TesterSpringConfig.class})
public class FirstTest
My configuration file:
#Configuration
#ComponentScan("com.testing")
#PropertySource("classpath:FirstTest.properties")
public class TesterSpringConfig
Any ideas?

Spring-boot default profile for integration tests

Spring-boot utilizes Spring profiles which allows to have separate config for different environments.
One way I use this feature is to configure the 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
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"};
}
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).
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.
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
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.
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.
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}</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.
UPDATE
added ${argLine} for this to work together with other plugins (e.g. jacoco)
To activate "test" profile write in your build.gradle:
test.doFirst {
systemProperty 'spring.profiles.active', 'test'
activeProfiles = 'test'
}
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
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.
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
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 {
....
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.
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

Categories

Resources