I am testing a functionality and has written a componentTest to ensure the component is working. The issue is when running the test #Configuration class is not loading for the test. Example:
TestClass:
#SpringBootTest(classes = Application.class)
public class ServiceTest{
//Do Something
}
#Configuration
public class ConfigurationClass{
#PostConstruct
public void doSomething(){
log.info("Test loading");
}
}
When running the application, I can see the logs printed on application startup. When I run the test, I don't see the logs printing from the ConfigurationClass. I tried using #ContextConfiguration(classes=ConfigurationClass.class) but no luck.
Practically , I want the configurationClass to be loaded before the tests are loaded.
When you run #SpringBootTest(classes = Application.class) with a specified class, its instructs the spring boot test engine to load only beans defined in Application.java which is supposed to (usually) be a class annotated with #Configuration annotation directly or indirectly.
If you want to just load the whole application context in the test, just use #SpringBootTest without any attributes. Now in this case it will scan packages up to one with #SpringBootConfiguration annotation (which presents on the class annotated with #SpringBootApplication and then will scan the packages down to load the configuration classes.
Of course you should make sure that the test will be able to find #SpringBootApplication class, for that you should put the test in the same package or beneath (of course the tests are in src/test/java as opposed to the src/main/java where you main class resides.
If I have a appplication.properties like:
url=someUrl
user=userOne
password=ABCD
But if I want to be able to set the password when testing to something else, lets say:
password=someTest
How do I do that?
I need to do this in one test
#Test
void checkSomething{
//change/override password before calling someMethod only for this test
someMethod();
}
You can create a testing profile file something like application-testing.properties and specify the overridden properties there.
Now while running the application you can specify use profile using
-Dspring.active.profiles=testing
There are multiple ways.
1st way: Spring Profile
aplication.yaml:
spring.profiles.active=dev
---
spring.profile=dev
url=someUrl
user=userOne
password=ABCD
---
spring.profile=test
password=someTest
Test class:
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles("test")
public class MyTestClass {...
2nd way: SpringBootTest Properties
#RunWith(SpringRunner.class)
#SpringBootTest(properties = { "password=someTest" })
public class MyTestClass {...
create another application.properties under src/test/resources thats all you need,
if you want to get properties to use in one method you can do i without involving spring :
InputStream input = Main.class.getResourceAsStream(yourproperties-path);
Properties prop = System.getProperties();
prop.load(input);
I want to test small parts of the application that rely on properties loaded with #Autowired and #ConfigurationProperties. I am looking for a solution loading only the required properties and not always the whole ApplicationContext.
Here as reduced example:
#TestPropertySource(locations = "/SettingsTest.properties")
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestSettings.class, TestConfiguration.class})
public class SettingsTest {
#Autowired
TestConfiguration config;
#Test
public void testConfig(){
Assert.assertEquals("TEST_PROPERTY", config.settings().getProperty());
}
}
Configuration Class:
public class TestConfiguration {
#Bean
#ConfigurationProperties(prefix = "test")
public TestSettings settings (){
return new TestSettings();
}
}
Settings Class:
public class TestSettings {
private String property;
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}
The properties file in the resource folder contains the entry:
test.property=TEST_PROPERTY
In my current setup config is not null, but no fields are available.
The reason the fields are not field should have something to do with the fact that I am not using Springboot but Spring.
So what would be the Springboot way to get this running?
edit:
The reason why I want to do this is: I have a parser that parses Textfiles, the regular expressions used are stored in a properties file.
To test this I would like to load only the properties needed for this parser which are in the exaple above the TestSettings.
While reading the comments I already noticed that this are no Unit tests anymore. However using the full Spring boot configuration for this small test seems a bit too much to me. That's why I asked if there is a posibilty to load only the one class with properties.
You need to annotate your TestConfiguraion with #EnableConfigurationProperties as follows:
#EnableConfigurationProperties
public class TestConfiguration {
#Bean
#ConfigurationProperties(prefix = "test")
public TestSettings settings (){
return new TestSettings();
}
}
Also you only need to include TestConfiguration.class in #ContextConfiguration of you SettingsTest class:
#TestPropertySource(locations = "/SettingsTest.properties")
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfiguration.class)
public class SettingsTest {
...
A couple points:
You don't need a "TestConfiguration" class in your main package, because all it's doing is configuring the "TestSettings" bean. You can do this simply by annotating the TestSettings class itself.
Normally you would load the context you need for the test using the #SpringApplicationConfiguration annotation, passing the name of your Application class. However, you said you don't want to load the whole ApplicationContext (though it's not clear why), so you need to create a special configuration class to do the loading only for tests. Below I call it "TestConfigurationNew" to avoid confusion with the TestConfiguration class that you had originally.
In the Spring Boot world, all properties are generally kept in the "application.properties" file; but it is possible to store them elsewhere. Below, I have specified the "SettingsTest.properties" file that you proposed. Note that you can have two copies of this file, the one in the main/resources folder, and the one in the test/resources folder for testing.
Change the code as follows:
TestSettings.java (in main package)
#Configuration
#ConfigurationProperties(prefix="test", locations = "classpath:SettingsTest.properties")
public class TestSettings {
private String property;
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}
SettingsTest.java (in test package)
#TestPropertySource(locations="classpath:SettingsTest.properties")
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = TestConfigurationNew.class)
public class SettingsTest {
#Autowired
TestSettings settings;
#Test
public void testConfig(){
Assert.assertEquals("TEST_PROPERTY", settings.getProperty());
}
}
TestConfigurationNew.java (in test package):
#EnableAutoConfiguration
#ComponentScan(basePackages = { "my.package.main" })
#Configuration
public class TestConfigurationNew {
}
This should now work the way you wanted.
you can actually just add #EnableConfigurationProperties to your #SpringBootTest directly.
eg:
#ActiveProfiles("test")
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestConfiguration.class)
#EnableConfigurationProperties
...
If you use Spring Boot, now you only need:
#RunWith(SpringRunner.class)
#SpringBootTest
No extra #ContextConfiguration, no extra class only for tests to EnableAutoConfiguration and EnableConfigurationProperties. You don't have to specify the configuration class to load, they will all be loaded.
But, ensure the properties entries you want to read in main/resources/application.yml is also present in test/resources/application.yml. Repetition is unavoidable.
Another way is:
Define a class of configuration only for tests, along with MyApplicationTest.java, at the same level. This class can be empty.
Like:
#EnableAutoConfiguration
#EnableConfigurationProperties(value = {
ConnectionPoolConfig.class
})
public class MyApplicationTestConfiguration {
}
And, in the class to load the autowired configuration.
Like:
#RunWith(SpringRunner.class)
//#SpringBootTest // the first, easy way
#ContextConfiguration(classes = MyApplicationTestConfiguration.class,
initializers = ConfigFileApplicationContextInitializer.class)
public class ConnectionPoolConfigTest {
#Autowired
private ConnectionPoolConfig config;
Basically, you:
use a specific configuration to #EnableConfigurationProperties and #EnableAutoConfiguration, listing all the #ConfigurationProperties files you want to load
in the test class, you load this configuration file of tests, with an initializer class defined by Spring to load application.yml file.
And, put the values to load in test/resources/application.yml. Repetition is unavoidable. If you need load another file, use #TestProperties() with a location. Note: #TestProperties only supports .properties files.
Both way works for configuration class loading values
either from application.yml/application.properties
or from another properties file, specified by PropertySource, like #PropertySource(value = "classpath:threadpool.properties")
Important
Last notes from Spring doc, as per here
Some people use Project Lombok to add getters and setters automatically. Make sure that Lombok does not generate any particular constructor for such a type, as it is used automatically by the container to instantiate the object.
Finally, only standard Java Bean properties are considered and binding on static properties is not supported.
That means, if you have lombok.#Builder without #NoArgsConstructor nor #AllArgsConstructor, properties injection will not happen because it only sees the invisible constructor created by #Builder. So, be sure to use none, or all of these annotations!
Unit test
To avoid having to load a Spring context, we can use the Binder class, which is also used internally by Spring anyway.
// A map of my properties.
Map<String, String> properties = new HashMap<>();
properties.put("my-prefix.first-property", "foo");
properties.put("my-prefix.second-property", "bar");
// Creates a source backed by my map, you can chose another type of source as needed.
ConfigurationPropertySource source = new MapConfigurationPropertySource(properties)
// Binds my properties to a class that maps them.
Binder binder = new Binder(source);
BindResult<MyConfiguration> result = binder.bind("my-prefix", MyConfiguration.class);
// Should return true if bound successfully.
Assertions.assertTrue(result.isBound);
// Asserts configuration values.
MyConfiguration config = result.get();
Assertions.assertEquals("foo", config.getFirstProperty());
Assertions.assertEquals("bar", config.getSecondProperty());
I am using Apache Maven and Spring. In the src/main/resources folder I have a properties file. These property values can have different values.
I am using PropertyPlaceholderConfigurer.
#Configuration
public class ResourceConfig {
#Bean
public PropertyPlaceholderConfigurer properties( ) {
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
ppc.setIgnoreResourceNotFound(true);
ppc.setLocations(new ClassPathResource[] {new ClassPathResource("propertiesFile")});
return ppc;
}
}
I replace these values at runtime:
#Configuration
public class DataSourceConfig {
#Value("${jdbc.url}")
private String jdbcUrlDefault;
}
This is just a sample. I have a main method:
public static void main(String[] args) {
// accept a properties file and replace those values defined in DataSourceConfig class
}
When Apache Maven builds the application the properties file will be on the classpath. The properties file are used during the unit testing. I want to some how replace the properties with a new properties file before the main program is launched for production.
I have seen some example of Properties.load(), but I don't want to do this. I want to accept a properties file through the main program that gets replaced, so the Spring side starts the PropertyPlaceholderConfigurer.
How can this be achieved?
you can place your test properties files in src/test/resources/. In test classes, it will use properties file from this location.
properties file placed here above location will not included in your classpath in final build.
use src/main/resources to place resource files that you want in main program
I have some unit tests with Spring. All the tests load a spring configuration file, and then add some more.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:beans.xml" }, inheritLocations = true)
public abstract class TestBase {
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:extraBeansOne.xml" }, inheritLocations = true)
public class TestOne extends TestBase {
#Test
public void testA() {
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:extraBeansTwo.xml" }, inheritLocations = true)
public class TestTwo extends TestBase {
#Test
public void testB() {
}
}
There's also a suite with the two tests:
#RunWith(Suite.class)
#Suite.SuiteClasses({ TestOne.class, TestTwo.class })
public class TestSuite {
}
In the common spring configuration file, I have a bean:
<beans ...>
<bean id="testBean" class="com.example.TestBean" />
<bean>
The problem is that, when I run the suite, the testBean gets loaded twice, once for each test class. Since it is defined in a common configuration file, is there any way to prevent it from loading multiple times?
No there is not really a chance to reuse them.
The Spring Junit Runner reuses the spring contexts over different tests, but only if there files are the same.
9.3.2.1 Context management and caching
...
Test classes provide an array containing the resource locations of XML configuration metadata - typically in the classpath - that is used to configure the application. These locations are the same as or similar to the list of configuration locations specified in web.xml or other deployment configuration files.
If this is very important for you and you are willing to spend time on it, then you can try to implement something that bases on the fact that application context can have a parent (like the two contexts in web applications, one defined for ContextLoaderListener the other defined for DispatcherServlet)) Then it is may possible to use/reuse the parent context in different test method dependend child contexts.
#See Spring Reference: Chapter 9.3.5.2 Context management and caching