How to add log messages in tests (spring boot)? - java

I want to add logging (for console) in my project, for a test in Spring Boot.
I have my test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestConfig.class})
public class MyTest {
private final static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(MyTest.class);
#Autowired
public UserDao userDao;
#Test
public void test1() {
LOGGER.info("info test");
LOGGER.debug("debug test");
}
}
and my test config:
#Configuration
#EnableJpaRepositories("example.dao")
#ComponentScan(basePackageClasses = { MyServiceImpl.class})
#EntityScan({"example.model"})
#Import({DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public class TestConfig {
}
I created an application.properties file in test/resource. Gradle sees my resource folder as a resource for tests.
Here's my application.properties:
logging.level.= INFO
logging.level.tests.= INFO
logging.level.org.hibernate= INFO
logging.level.org.springframework= INFO
logging.level.org.apache.cxf= INFO
But when I run my test, I have:
16:59:17.593 [main] INFO tests.MyTest - info test
16:59:17.594 [main] DEBUG tests.MyTest - debug test
in the console. Why?
I set just 'INFO'(logging.level.= INFO). Why is 'DEBUG' in the console? How can to set it just to'INFO'?

It's a two step proccess.
First, add spring-boot-starter-test as a test dependency. Then tell JUnit to use the ContextLoader from the just added dependency.
Change
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestConfig.class})
public class MyTest {
...
to
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestConfig.class},
loader = SpringApplicationContextLoader.class)
public class MyTest {
...
The context loader lives in spring-boot-starter-test added in the first step and does the initialization magic normally done by the ApplicationBootstrapper.
Depending on the spring-boot version you are using there are some other possibilities (e.g. using #SpringApplicationConfiguration in place of #ContextConfiguration). You can read more about that in this spring blog: https://spring.io/blog/2016/04/15/testing-improvements-in-spring-boot-1-4

Try adding #SpringBootConfiguration instead of #ContextConfiguration on your MyTest class.
Follow the example in the Reference doc:
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing-spring-boot-applications
A Spring Boot application is just a Spring ApplicationContext so nothing very special has to be done to test it beyond what you would normally do with a vanilla Spring context. One thing to watch out for though is that the external properties, logging and other features of Spring Boot are only installed in the context by default if you use SpringApplication to create it.

Related

Spring override bean of another module

In my multimodule project I have integration tests as seperate module. That tests have application jar added as dependency. Is it possible to override application bean definition from integration tests?
In application I have following Bean (standard java mail sender configuration)
#Configuration
public class MailConfiguration {
#Bean
public JavaMailSender javaMailService() {
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
//standard mail configuration using properties
}
}
Now all my integration tests extends BaseIntegrationTest that loads test configuration classess
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes = { AppTestConfiguration.class, MailTestConfiguration.class})
public class BaseIntegrationTest {
}
And finally in my MailTestConfiguration I define another JavaMailSender
#Primary
#Bean
#Profile(TestProfiles.MAIL_GREEN_SMTP)
public JavaMailSender javaMailService() {
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
javaMailSender.setHost("localhost");
javaMailSender.setPort(3025);
return javaMailSender;
}
It is working when I run the tests from application itself. When I run the tests from another module the bean is not overriden.
I am aware that AppConfiguration class defined inside application cannot component scan the integration tests config classes so I also load AppTestConfiguration.
#Configuration
#ComponentScan(basePackages = {"..."})
public class AppTestConfiguration extends AppConfiguration {
}
How to make it work?
Two things you should check:
Is the right Spring profile enabled when you run the tests from another module?
Is the given configuration in the scan path when you run the tests from another module?
Sure I messed up with profiles. In my BaseIntegrationTest I defined active profile based on configuration. I also printed which profile gets resolved there (correct profile name was printed)
#BeforeClass
public static void init() {
System.setProperty(DEFAULT_PROFILES_PROPERTY_NAME, ProfileResolver.getActiveProfiles());
}
After you convinced me that it should work I rechecked the config and found that I also added spring.profiles.active in properties. After deleting this config everything work as expected. The other way is to use AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME instead of AbstractEnvironment.DEFAULT_PROFILES_PROPERTY_NAME

ServletContext cannot open properties file when executing JUnit test

I'm testing a REST controller using JUnit 4 and MockMvc. When I've written the test a few weeks ago, everything worked as expected. I've done some modifications in my code but I didn't change the JUnit test. Now, when I'm trying to run my tests, I have the error:
Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/application.properties]
Here is my code:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = MyServerApplication.class)
#SpringBootTest
#Transactional
public class MovieControllerTest {
private MockMvc mockMvc;
#Autowired
private MovieRepository movieRepository;
#Autowired
private WebApplicationContext wac;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
// Some tests
}
And my main class:
#SpringBootApplication
public class MyServerApplication{
public static void main(String[] args) {
SpringApplication.run(MyServerApplication.class, args);
}
}
My application.properties file is located in src/main/resources. I didn't move this file, I didn't do anything but add some code in my services and add some properties in my file.
I read SO questions & doc, and tried these solutions:
Check that src/main/resources is still in my test classpath
Add #PropertySource("classpath:application.properties") under the annotations in my test ; it didn't work so I tried to create a src/test/resources with a copy of application.properties inside, as suggested in one post
Add #PropertySource("classpath:application.properties") in the main class instead of the test class
Add #WebAppConfiguration annotation
Add #WebMvcTest annotation
I didn't try all of these solutions at the same time of course, I removed the added code after each failure.
I can still run my code without any issue though, only the test class results in FileNotFoundException.
How to solve this? And why do I have an issue with the test class but everything working fine when I run my server?
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = MyServerApplication.class)
#SpringBootTest
#Transactional
public class MovieControllerTest { ... }
This is what you have on your test class. When using #SpringBootTest you shouldn't be using #ContextConfiguration (see testing chapter of the Spring Boot Reference Guide).
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
#Transactional
public class MovieControllerTest { ... }
I would also suggest you use Spring Boot for testing instead of trying to do things manually. For mock mvc testing Spring Boot applications there are special slices and setup already done for you.
To enable this add #AutoConfigureMockMvc to your test and put #Autowired on the MockMvc field (and remove the setup in your #Before method).

Unit testing of Spring Boot Actuator endpoints not working when specifying a port

recently I changed my spring boot properties to define a management port.
In doing so, my unit tests started to fail :(
I wrote a unit test that tested the /metrics endpoint as follows:
#RunWith (SpringRunner.class)
#DirtiesContext
#SpringBootTest
public class MetricsTest {
#Autowired
private WebApplicationContext context;
private MockMvc mvc;
/**
* Called before each test.
*/
#Before
public void setUp() {
this.context.getBean(MetricsEndpoint.class).setEnabled(true);
this.mvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
/**
* Test for home page.
*
* #throws Exception On failure.
*/
#Test
public void home()
throws Exception {
this.mvc.perform(MockMvcRequestBuilders.get("/metrics"))
.andExpect(MockMvcResultMatchers.status().isOk());
}
}
Previously this was passing. After adding:
management.port=9001
The tests started failing with:
home Failed: java.lang.AssertionError: Status expected: <200> but was: <404>
I tried changing the #SpringBootTest annotation with:
#SpringBootTest (properties = {"management.port=<server.port>"})
Where is the number used for the server.port. This didn't seem to make any difference.
So then changed the management.port value in the property file to be the same as the server.port. Same result.
The only way to get the test to work is remove the management.port from the property file.
Any suggestions/thoughts ?
Thanks
For Spring Boot 2.x the integration tests configuration could be simplified.
For example simple custom heartbeat endpoint
#Component
#Endpoint(id = "heartbeat")
public class HeartbeatEndpoint {
#ReadOperation
public String heartbeat() {
return "";
}
}
Where integration test for this endpoint
#SpringBootTest(
classes = HeartbeatEndpointTest.Config.class,
properties = {
"management.endpoint.heartbeat.enabled=true",
"management.endpoints.web.exposure.include=heartbeat"
})
#AutoConfigureMockMvc
#EnableAutoConfiguration
class HeartbeatEndpointTest {
private static final String ENDPOINT_PATH = "/actuator/heartbeat";
#Autowired
private MockMvc mockMvc;
#Test
void testHeartbeat() throws Exception {
mockMvc
.perform(get(ENDPOINT_PATH))
.andExpect(status().isOk())
.andExpect(content().string(""));
}
#Configuration
#Import(ProcessorTestConfig.class)
static class Config {
#Bean
public HeartbeatEndpoint heartbeatEndpoint() {
return new HeartbeatEndpoint();
}
}
}
For Spring boot test we need to specify the port it needs to connect to.
By default, it connects to server.port which in case of actuators is different.
This can be done by
#SpringBootTest(properties = "server.port=8090")
in application.properties we specify the management port as below
...
management.server.port=8090
...
Did you try adding the following annotation to your test class?
#TestPropertySource(properties = {"management.port=0"})
Check the following link for reference.
Isn't there an error in the property name?
Shouldn't be
#TestPropertySource(properties = {"management.server.port=..."}) instead of #TestPropertySource(properties = {"management.port=.."})
The guide stated that this can be achieved with #AutoConfigureMetrics.
And I moved with this.
Regardless of your classpath, meter registries, except the in-memory backed, are not auto-configured when using #SpringBootTest.
If you need to export metrics to a different backend as part of an integration test, annotate it with #AutoConfigureMetrics.
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.testing.spring-boot-applications.metrics
Had the same issue, you just have to make the management.port null by adding this in your application-test.properties (set it to empty value)
management.port=
Make sure you use the test profile in your JUnit by annotating the class with
#ActiveProfiles("test")
Try using
#SpringBootTest(properties = {"management.port="})
Properties defined in the #SpringBootTest annotation have a higher precedence than those in application properties. "management.port=" will "unset" the management.port property.
This way you don't have to worry about configuring the port in your tests.
I was facing the same issue and tried several things but this is how I was able to solve mine without making any change in the application.yaml
Sample actuator endpoint
#Component
#RestControllerEndpoint(id = "endpoint")
public class SampleEndpoint
{
#GetMapping
public String sampleEndpoint(){
return ""
}
}
Unit test case
#RunWith(SpringRunner.class)
#SpringBootTest(
classes = {SampleEndpointTest.Config.class},
properties = {"management.server.port="}
)
#AutoConfigureMockMvc
public class SampleEndpointTest
{
#Autowired
private MockMvc mockMvc;
#SpringBootApplication(scanBasePackageClasses = {SampleEndpoint.class})
public static class Config
{
}
#Test
public void testSampleEndpoint() throws Exception
{
mockMvc.perform(
MockMvcRequestBuilders.get("/actuator/enpoint").accept(APPLICATION_JSON)
).andExpect(status().isOk());
}
Since now info endpoint must be enabled manually make sure the SpringBootTest tag includes this in properties, like this:
#SpringBootTest(
properties = {
"management.info.env.enabled=true" ,
"management.endpoints.web.exposure.include=info, health"
})
I had this problem recently, and as none of the above answers made any sense to me, I decided to do a bit more reading. In my case, I had already defined both server.port and management.server.port as 8091 in my test application-test.yaml file, and could not understand why my test was getting a connection refused error message.
It turns out that instead of using the annotation #SpringBootTest() I needed to use #SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) - which causes the port numbers in the yaml file to be used. This is briefly discussed in the manual. Quoting the relevant section:
DEFINED_PORT — Loads an EmbeddedWebApplicationContext and provides a real servlet environment. Embedded servlet containers are started and listening on a defined port (i.e from your application.properties or on the default port 8080).
It seems in SpringBootTest the default is to avoid starting a real servlet environment, and if no WebEnvironment is explicitly specified then SpringBootTest.WebEnvironment.MOCK is used as a default.
After a long search: There is this nice Springboot annotation called #LocalManagementPort!
It works similar to #LocalServerPort but for actuator endpoins.
An example config would look as follows
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MetricsIT {
#Autowired
RestTemplateBuilder restTemplateBuilder;
#LocalManagementPort
int managementPort;
#Test
public void testMetrics(){
ResponseEntity<String> response = restTemplateBuilder
.rootUri("http://localhost:" + managementPort + "/actuator")
.build().exchange("/metrics", HttpMethod.GET, new HttpEntity<>(null), String.class);
}
}

Override application.properties for integration tests in spring-boot app

I have a standard spring-boot app and I want to use MS SQL database for the production environment, whereas for integration tests I'd like to use h2 databse. The problem is that I wasn't able to find out, how to override the default application.properties file. Even though I was trying to follow some tutorials, I didn't come up with the right solution...maybe I'm just missing something...
The main class:
#SpringBootApplication
#EnableTransactionManagement
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication .class, args);
}
}
and the class with tests:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyApplication.class)
#WebIntegrationTest
public class MessageControllerTest {
#Autowired
MessageRepository messageRepository;
...
...
...
#Test
public void testSomething(){
...
...
...
...
}
}
So the question is, how to force the spring-boot to use application-test.properties file when running the tests, instead of application.properties, which should be used during the run time.
I tried for example to replace #WebIntegrationTest annotation with #TestPropertySource(locations="classpath:application-test.properties"), but this results in java.lang.IllegalStateException: Failed to load ApplicationContext.
Assuming you have a application-test.properties file in your app.
I do it in two ways :
1.CLI JVM Args
mvn spring-boot:run -Drun.jvmArguments="-Dspring.profiles.active=test
add the application-test.properties as an active profile.
add the spring.profiles.active=test in the application.properties and it will load your application-test.properties file.
As you pointed to in your answer annotate a class test with a specific active profile ( which is not suitable when having a large test classes i think ) #ActiveProfiles("test")
Actually it was pretty easy...after several hours of trying, I've realized that I just needed to annotate my test class with #ActiveProfiles("test") annotation.
#ActiveProfiles("test")
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MyApplication.class)
#WebIntegrationTest
public class MessageControllerTest {
#Autowired
MessageRepository messageRepository;
...
...
...
#Test
public void testSomething(){
...
...
...
...
}
}

How to run code before SpringJUnit4ClassRunner context initialization?

In my application I initialize a property before spring application startup as follows:
MapLookup.setMainArguments(new String[] {"logging.profile", profile}); //from args
SpringApplication.run(source, args);
(just for reference: it is used for log4j2 logging, which must be set before spring starts to initialize).
Now I want to run an #IntegrationTest, but use the same logging configuration. Obviously I cannot use the code above, as a JUnit test is not executed using SpringApplication.run.
So, how could I initialize code before a #RunWith(SpringJUnit4ClassRunner.class) starts?
Note: BeforeClass does not work as this is executed after spring context startup.
You can run the initialization in a static initializer. Static initializer will run after JUnit loads the test class and before JUnit reads any annotations on it.
Alternatively you can extend SpringJUnit4ClassRunner with your own Runner initialize in it first and then run SpringJUnit4ClassRunner
I had a slightly different problem. I need to deploy something to my service after the Spring context is loaded. Solution use a custom config class for the test and run the deployment within a #PostConstruct Method.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfig.class, loader = AnnotationConfigContextLoader.class)
public class JunitTest {
#Configuration
#ComponentScan(basePackages = { "de.foo })
public static class TestMConfig {
#Autowired
private DeploymentService service;
#PostConstruct
public void init() {
service.deploy(...);
}
}
#Test
public void test() {
...
}
}
Maybe this helps, someone, sometime, somewhere ;)

Categories