How to get properties from .env file in spring boot test - java

I am trying to setup tests for my spring-boot application. In regular execution I get some values from .env file that I've specified in run configuration and get them like so:
#Value("${jdbc.url}")
private String jdbcUrl;
But when I try to run the simplest of tests, it fails with the exception :
Failed to load ApplicationContext
java.lang.IllegalStateException: Failed to load ApplicationContext........
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaConfig': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'jdbc.url' in value "${jdbc.url}"
How do I load properties from the environment in SpringBootTest?
Here's my test:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {App.class})
public class TestingWebApplicationTest {
#Test
public void contextLoads() {
}
}

Try adding your property to this location /src/test/resources/application-test.properties
Add an #ActiveProfiles("test") annotation on the test class and it should be picked up.
See screenshot below;
You can use a profile specific application-{profile}.properties file

Related

Spring Boot YAML binding: fail when using yml extension

i cannot start my spring-boot application, because an exception with the following messagge is thrown"Error creating bean with name 'application': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'tracer.datadog' in value
The reason is that spring-boot cannot find the "tracer.datadog" prop specificied on the application class:
#SpringBootApplication
#EnableMongoAuditing
public class Application {
#Value("${tracer.datadog}")
private boolean datadog;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#PostConstruct
private void jaegerTracer() {
if (!datadog) {
GlobalTracer.registerIfAbsent(Configuration.fromEnv().getTracer());
}
}
}
But that property is correctly defined over the application.yml file:
tracer:
datadog: true
However, changing the extension to .YAML spring-boot "find" the file and the application starts fine.
Does anyone can tell me how can i do to solve this problem and use the .yml extension? using the .yaml extension is not a possibility because of company standards

#TestPropertySource Class Could not resolve placeholder

I'm creating a TestClass using a class Propiedades which has #PropertySource annotation.
#Component
#PropertySource("file:${services.properties}test-app/.properties")
public class Propiedades {
#Value("${db.name}")
public String dbName;
#Value("${db.jndi}")
public String dbJNDI;
#Value("${db.owner}")
public String dbOwner;
}
As you can see I'm loading the .properties file from a external location relative to the server where the app is deployed. services.properties is a env variable for the server which has a route to a specific folder.
Here is my test class
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = Propiedades.class)
#TestPropertySource(locations = "/application-test.properties")
public class IncidenciaServicioServiceImplTest {
#Autowired
Propiedades propiedades;
#Test
public void isPropertySetup() {
String output = propiedades.dbName;
Assert.assertEquals("testDb", output);
}
}
When running the test I get the following error.
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [Propiedades]; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'services.properties' in value "file:${services.properties}test-app/.properties"
Why is it trying to load the properties from that path?, shouldn't it be reading from src/test/resources/application-test.properties?
As far as I know Spring does not work like you expect. It always tries to load properties specified in #PropertySource first then overrides them with values provided with #TestPropertySource annotation.
So one of the ways to workaround your issue is to use ignoreResourceNotFound flag for #PropertySource annotation:
#PropertySource("file:${services.properties}test-app/.properties", ignoreResourceNotFound = true)
public class Propiedades {
// ... your properties
}
In this case Spring will ignore the fact it can not find the properties specified with #PropertySource and continue using values from #TestPropertySource
Or alternatively, you need to provide value for your ${services.properties} env variable while tests run so it is resolved to correct path to property file.

Spring Boot Testing Application Yaml java.lang.IllegalStateException: Tomcat connector in failed state

I am new to testing the spring application, not sure how to test a application profiles defined application.yml in my spring boot app.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {WebApplication.class})
#WebAppConfiguration
public class ApplicationSettingsTest {
#Test
public void applicationPicksRightTeamProfile() throws Exception {
WebApplication.main(new String[] { "--spring.profiles.active=FalconDev1" });
String output = this.outputCapture.toString();
assertThat(output, containsString("falcondev.io"));
}
#Test
public void applicationPicksRightDefaultProfile() throws Exception {
WebApplication.main(new String[0]);
String output = this.outputCapture.toString();
assertThat(output, containsString("defaultdev.io"));
}
}
My first test appears to be passing, but the second tests is failed with multiple errors,
org.apache.catalina.LifecycleException: Failed to start component
[Connector[org.apache.coyote.http11.Http11NioProtocol-8090]]
Caused by: org.apache.catalina.LifecycleException: service.getName():
"Tomcat"; Protocol handler start failed at
org.apache.catalina.connector.Connector.startInternal(Connector.java:1014)
~[tomcat-embed-core-7.0.59.jar:7.0.59]
Caused by: java.net.BindException: Address already in use at
sun.nio.ch.Net.bind(Native Method) ~[na:1.6.0_65]
org.springframework.boot.context.embedded.EmbeddedServletContainerException:
Unable to start embedded Tomcat servlet container at
org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer.start(TomcatEmbeddedServletContainer.java:165)
Caused by: java.lang.IllegalStateException: Tomcat connector in failed
state at
org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer.start(TomcatEmbeddedServletContainer.java:159)
~[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE]
EDIT:
application.yml
server:
port: ${port:8090}
I do understood that application has actually started on port 8090 and my second attempting to run again on the same port, which I don't want.
so how to tell in my tests to just load the application context rather starting the real application.Any ideas are appreciated.
The following code will run spring boot embedded container with random port:
#IntegrationTest("server.port:0")
In order to set Spring active profile in test you can use ActiveProfilesannotation.
#ActiveProfiles("test")
Set spring.profiles.active in application properties or via environment variable using --spring.profiles.active=test.
Spring contains Environment bean. If you want to obtain profiles in your code you can use following code:
#Autowired
Environment environment;
String[] profiles = environment.getActiveProfiles();
The following code WebApplication.main(new String[0]); is incorrect. You do actually run 2 Spring context.
The problem hear is that you start by heands the Spring context with the instructionWebApplication.main(....);
The key point hear is that the test class asserts that you have a test context, that will be a Spring context for testing in this stage you don't have the embedded tomcat started for this I can suggest to use #WebIntegrationTest without #WebAppConfiguration.
In your case in the first test method you will have a Spring context for test and then you start another spring contest that start also tomcat to the 8090 port, after this the second test method heredity the test context, but in the test method you restart the context and another tomcat on the same port and in this stage you get the exception.
The #IntegrationTest("server.port:0") don't work because this annotation work on the test context and not on the context that you start on you heands.
The my advice is refactoring the your code in this way:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {WebApplication.class})
#WebIntegrationTest(randomPort = true)
#ActiveProfiles("FalconDev1")
public class ApplicationSettingsTest {
#Test
public void applicationPicksRightTeamProfile() throws Exception {
String output = this.outputCapture.toString();
assertThat(output, containsString("falcondev.io"));
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {WebApplication.class})
#WebIntegrationTest(randomPort = true)
public class ApplicationSettingsTest {
#Test
public void applicationPicksRightDefaultProfile() throws Exception {
String output = this.outputCapture.toString();
assertThat(output, containsString("defaultdev.io"));
}
}
Two test classes that map the you two siute of test, in the first you can test the profile FalconDev1 in isolation from the default and in the second the other profile. in this way you can benefit of the spring abstractions.
I hope tha this can help you

Spring & JUnit | How to disable spring test context caching for a specific test class?

My code has many integration tests classes - ServiceTest1, ServiceTest2 ... , ServiceTestN.
All tests are executed with spring using the same test context (MyAppContext.xml).
As shown in the snippet below, ServiceTest1 test context requires one of its beans to be overridden, and that change is relevant ONLY for ServiceTest1.
When executed, ServiceTest1 works as expected. However, when executing the other tests (e.g. ServiceTest2 in the snippet) it fails with spring initialization errors, since spring is caching MyAppContext.xml test context, and ServiceTest1 test context manipulation is conflicting with the other tests.
I'm looking for a way to avoid caching of ServiceTest1 test context (such as making its caching key unique, based on #ContextConfiguration).
Note that fully disabling caching is not a wanted solution.
Any ideas?
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class ServiceTest1 {
// some tests ...
#Configuration
#ImportResource({"classpath*:MyAppContext.xml"})
static class Context {
#Bean
#Primary
ServiceOne serviceOne() {
// instantiate something ...
}
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath*:MyAppContext.xml"})
public class ServiceTest2 {
// some tests ...
}
Thanks!
EDIT:
Spring initialization errors are:
java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Failed to import bean definitions from URL location [classpath:/WEB-INF/applicationContext-security]
Offending resource: URL [file:/path/to/MyAppContext.xml]; nested exception is org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Duplicate detected.
Offending resource: class path resource [WEB-INF/applicationContext-security.xml]
Use the #DirtiesContext annotation, add this at the class level.
It will then refresh the spring application context after the test class has executed.
Documentation: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/annotation/DirtiesContext.html

org.springframework.beans.factory.NoSuchBeanDefinitionException or Failed to load ApplicationContext

I am new to Junit test cases. Could some one please help me with the below issue.
My project structure is as below:
src/main/java -> sources
src/main/resources -> /Spring/Context -> contains application-context.xml
src/test/java -> all junit test classes
I am trying to load application-context.xml using annotation
#ContextConfiguration(locations = {"classpath:*/application-context.xml"})
#RunWith(SpringJUnit4ClassRunner.class)
public class LoadTest {
#Autowired
ApplicationContext context;
#Test
public void test() {
ProcessManager processManager=(ProcessManager)context.getBean("processManager");
//Rest of the code
}
}
If I run the above test I am getting NoSuchBeanDefinitionException
I tried changing
#ContextConfiguration(locations ={"classpath:spring/context/application-context.xml"})
If I do so I am getting Failed to load ApplicationContext error
Instead of trying to autowire the Context itself just to get the real bean (ProcessManager) out of it, simply go for the bean itself:
#Autowired
ProcessManager processManager;
and just call an existing function on the autowired bean to verify that your new Spring test is working fine.
(what you were trying to do is getting the ApplicationContext out of the ApplicationContext)

Categories