Dependency Injection not working in SpringBoot with Junit 5 Integration test - java

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.

Related

#WebMvcTest creating more than one Controller for some reason

I'm trying to create a controller test with #WebMvcTest, and as I understand, when I put #WebMvcTest(ClientController.class) annotation of the test class it should not create a whole lot of beans, but just ones that this controller requires.
I'm mocking the bean this controller requires with #MockBean, but somehow it fails with an exception that there's 'No qualifying bean' of another service that does not required by this controller but by another.
So this test is failing:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = ClientController.class)
public class ClientControllerTest {
#MockBean
ClientService clientService;
#Test
public void getClient() {
assertEquals(1,1);
}
}
I've created an empty Spring Boot project of the same version (2.0.1) and tried to create test over there. It worked perfectly.
So my problem might be because of the dependencies that my project has many, but maybe there's some common practice where to look in this situation? What can mess #WebMvcTest logic?
I've found a workaround. Not to use #WebMvcTest and #MockBean, but to create everything by hand:
//#WebMvcTest(ClientController.class)
#RunWith(SpringRunner.class)
public class ClientControllerTest {
private MockMvc mockMvc;
#Mock
ClientService clientService;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(
new ClientController(clientService)
).build();
}
works with Spring 1.4.X and with Spring Boot 2.X (had different exception there and there), but still doesn't explain why #WebMvcTest doesn't work

junit couldn't load the value in resource folder

I am using MockitoJUnit library to test my controller. there are some variables which are declared as:
#Value("${mine.port}")
private Integer port;
when I run my test, it could not find the value mine.port in the resource folder as properties file. My controller is no problem to run, but the port number is null when I run my contoller junit test.
I used below annotation on my class:
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(classes = myControl.class)
#WebAppConfiguration
#AutoConfigureMockMvc
I used below method in my junit test
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.standaloneSetup(myController)
.build();
}
#Test
public void getName() throws Exception {
MockHttpServletRequestBuilder request =
MockMvcRequestBuilders.get("/service")
.contentType(MediaType.APPLICATION_JSON_VALUE);
MvcResult result = mockMvc.perform(request)
.andExpect(status().isOk()).andReturn(); }
I am confused, does anyone know if I am missing some settings? thx!
No one of the specified annotations will start a Spring container :
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(classes = myControl.class)
#WebAppConfiguration
#AutoConfigureMockMvc
To write a test slice focused on the controller don't use a Mockito runner : #RunWith(MockitoJUnitRunner.class) otherwise you could not start a Spring container. MockitoJUnitRunner is for unit tests, not integration tests.
To know when and how to write unit and integration tests with Spring Boot you could read this question.
Instead of, use the #WebMvcTest annotation with a Spring runner such as :
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#WebMvcTest
public class WebLayerTest {
...
}
You can rely on this Spring guide to have more details.

What is the use of contextLoads method in Spring Boot Junit Testcases?

This method is empty in all my JUnit test cases. What is the use of this method?
Sonarqube is complaining
"Add a nested comment explaining why this method is empty, throw an UnsupportedOperationException or complete the implementation."
I can bypass this by adding some comment but I just want to know why it is necessary.
When you build a Spring boot application using Spring Initializer. It auto creates a test class for you with contextLoads empty method.
#SpringBootTest
class ApplicationContextTest {
#Test
void contextLoads() {
}
}
Note the use of #SpringBootTest annotation which tells Spring Boot to look for a main configuration class (one with #SpringBootApplication, for instance) and use that to start a Spring application context. Empty contextLoads() is a test to verify if the application is able to load Spring context successfully or not.
If sonarqube is complaining about the empty method then you can do something like this to verify your controller or service bean context:-
#SpringBootTest
public class ApplicationContextTest {
#Autowired
private MyController myController;
#Autowired
private MyService myService;
#Test
public void contextLoads() throws Exception {
assertThat(myController).isNotNull();
assertThat(myService).isNotNull();
}
}
Use different runner,
if you are using SpringRunner.class, use an alternative one such as MockitoJUnitRunner.class or MockitoJunitRunner.class rather then SpringRunner.class
#Runwith(SpringRunner.class)
#Runwith(JUnit4ClassRunner.class)
#Runwith(MockitoJUnit4Runner.class)
#Runwith(MockitoJUnitRunner.class)

#Autowired Bean is NULL in Spring Boot Unit Test

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;

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).

Categories