I am working on setting up an Integration test with SpringBoot and Junit5 following this tutorial
But when I run this test file without #RunWith(SpringRunner.class) annotation gives NullPointerException since RecordService is not being injected.
#ExtendWith(SpringExtension.class)
#SpringBootTest
#DefaultTestAnnotations // This is my meta-annotations
public class RecordServiceImplTest {
#Autowired
private RecordService recordService; // This is null.
#Test
public void whenSearchParametersAreProvided_ItShouldGetTheGoldenRecord() throws MdmMatchServiceException {
GoldenRecordDTO searchParams = new GoldenRecordDTO();
searchParams.setCountryCode("CN");
searchParams.setName("neeraj");
assertNotNull(recordService.getGoldenRecord(searchParams));
}
}
Is it mandatory to have #RunWith(SpringRunner.class) for running Integration tests?
I suspect that you imported JUnit4 annotation org.junit.Test instead of JUnit5 annotation: org.junit.jupiter.api.Test.
I write the test for the Spring Boot and have 2 classes that are testing the API and the repository. The skeletons are provided below,
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
#AutoConfigureMockMvc
public class AppointmentAPITest {
/*
* we need a system to simulate this behavior without starting a
* full HTTP server. MockMvc is the Spring class that does that.
* */
#Autowired
private MockMvc mockMvc;
#MockBean
private AppointmentAPI api;
// now the tests are going on
}
The repository test class,
#ActiveProfiles("test")
#RunWith(SpringRunner.class)
#DataJpaTest
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class AppointmentRepositoryTest {
#Autowired
private TestEntityManager entityManager;
#Autowired
private AppointmentRepository repository;
// write the test here
}
How do I use a single class to run them all? For example, if the class will be AppointmentApplicationTests,
#RunWith(SpringRunner.class)
#SpringBootTest
public class AppointmentApplicationTests {
#Test
public void contextLoads() {
}
}
What will be the best way to configure this class so it calls all the tests in the API and repo classes?
I think simpliest way would be to create a Suite to have a collection of tests to be run, like:
JUnit 4
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
#RunWith(Suite.class)
#Suite.SuiteClasses({
some.package.AppointmentRepositoryTest,
some.package.AppointmentApplicationTests
})
public class MySuite {
}
Junit5
#RunWith(JUnitPlatform.class)
#SelectClasses({some.package.AppointmentRepositoryTest,
some.package.AppointmentAPITest.class})
public class JUnit5TestSuiteExample {
}
However this is not alwasy the best way to do it. Consider also to get acquainted with howto create test profile for maven toe perform a bunch of tests or packages.
I am new to JUnit and automated testing and really want to get into automating my tests. This is a Spring Boot application. I have used Java Based Annotation style instead of XML based configuration.
I have a test class in which I'd like to test a method which retrieves a response based on a users' input.
Testing class:
#RunWith(SpringRunner.class)
#SpringBootTest
public class SampleTest(){
#Autowired
private SampleClass sampleClass;
#Test
public void testInput(){
String sampleInput = "hi";
String actualResponse = sampleClass.retrieveResponse(sampleInput);
assertEquals("You typed hi", actualResponse);
}
}
Inside my "SampleClass" I have autowired a bean like this.
#Autowired
private OtherSampleClass sampleBean;
Inside my "OtherSampleClass" I have annotated a method like so:
#Bean(name = "sampleBean")
public void someMethod(){
....
}
The issue I'm having is when I try to run the test without the #RunWith and #SpringBootTest annotations when I try to run the test my variables annotated #Autowired are null. And when I try to run the test with those annotations RunWith & SpringBootTest then I get an
IllegalStateException caused by BeanCreationException: Error creating
bean with name "sampleBean" AND failure to load application context
caused by BeanInstantiationException.
The code works 'properly' when I try to use it as a user would so I can always test this way but I think automated tests would be good for the longevity of the program.
I have used the Spring Boot Testing Docs to assist me in this.
The following config works for me.
File: build.gradle
testCompile("junit:junit:4.12")
testCompile("org.springframework.boot:spring-boot-starter-test")
File: MYServiceTest.java
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
#SpringBootTest(classes = Application.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles("test")
#RunWith(SpringRunner.class)
public class MYServiceTest {
#Autowired
private MYService myService;
#Test
public void test_method() {
myService.run();
}
}
It's best to keep Spring out of your unit tests as much as possible. Instead of Autowiring your bean just create them as a regular object
OtherSampleClass otherSampleClass = mock(OtherSampleClass.class);
SampleClass sampleClass = new SampleClass(otherSampleClass);
But for this you need to use the Constructor injection instead of Field Injection which improves testability.
Replace this
#Autowired
private OtherSampleClass sampleBean;
With this
private OtherSampleClass sampleBean;
#Autowired
public SampleClass(OtherSampleClass sampleBean){
this.sampleBean = sampleBean;
}
Take a look at this answer for an other code example
No need to inject (#Autowired private SampleClass sampleClass;) your actual class which you are testing, and remove SpringBootTest annotation, SpringBootTest annotation used for integration test cases.
find the following code will help you.
#RunWith(SpringRunner.class)
public class SampleTest(){
private SampleClass sampleClass;
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).
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);
}
}