Properties in RestController is not accessible and Null in Junit - spring-boot

When i run my junit for Rest Controller class, properties in Rest controller is throwing Nullpointer exception. Below is my sample code. when i run the testFileStatus() method of TestingControllerTest calass, i am getting Nullpointer exception. Kindly help to resolve the issue. Any suggestion why i can't able to access the property even though it is available in test application.properties.
enter image description here
#RestController
#CrossOrigin(origins = "*", allowedHeaders = "*")
public class TestingController {
#Value("${file.dir.status}")
private String fileDirectoryStatus;
#GetMapping(value="/filedetails")
public String getFileDetails(.....){
if(fileDirectoryStatus.equalsIgnoreCase("true")) { //NullpointerException in Junit test
//code to process file
}
}
}
src/main/resources/application.properties:
file.dir.status=false
src/test/resources/application.properties:
file.dir.status=false
#SpringBootTest
#TestPropertySource(
locations = "classpath:resources/application.properties")
public class TestingControllerTest{
#InjectMocks
private TestingController testingController;
#Test
public void testFileStatus() {
String status=testingController.getFileDetails(...);
}
}

Related

Spring Boot JUnit tests fail with Status expected:<200> but was:<404>

For some time I've been struggling to make JUnit tests for my rest controller. For some reason, every time I try to run them I get the error Status expected:<200> but was:<404>. Here is my controller:
#RestController
#RequestMapping("/travels")
#RequiredArgsConstructor
public class TravelController {
private final TravelService travelService;
private final TravelOutputDtoMapper travelOutputDtoMapper;
#GetMapping
public List<TravelOutputDto> getAll() {
List<Travel> travels = travelService.getAll();
return travels.stream()
.map(travelOutputDtoMapper::travelToTravelOutputDto)
.collect(Collectors.toList());
}
}
And here is my test:
#ExtendWith(SpringExtension.class)
#WebMvcTest(controllers = TravelController.class)
#ContextConfiguration(classes = {
TravelOutputDtoMapper.class,
TravelOutputDtoMapperImpl.class
})
class TravelControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private TravelService travelService;
#Autowired
private TravelOutputDtoMapper travelOutputDtoMapper;
#Test
void testGetAll() throws Exception {
List<Travel> travels = mockTravelList();
Mockito.when(travelService.getAll()).thenReturn(travels);
mockMvc.perform(get("/travels"))
.andExpect(status().isOk());
}
private List<Travel> mockTravelList() {
// Dummy travel list
}
}
I think the reason is connected with TravelOutputDtoMapper as if I remove it from the controller and don't try to inject it the tests are passing, but I cannot find any information why it is doing it. The autowired mapper has an instance and works just fine.
Here is the Mapper:
#Mapper(componentModel = "spring")
public interface TravelOutputDtoMapper {
#Mapping(target = "from", source = "entity.from.code")
#Mapping(target = "to", source = "entity.to.code")
TravelOutputDto travelToTravelOutputDto(Travel entity);
}
The #ContextConfiguration annotation is used for a different purpose:
#ContextConfiguration defines class-level metadata that is used to determine how to load and configure an ApplicationContext for integration tests.
Using Spring Boot and #WebMvcTest there's no need to manually specify how to load the context. That's done for you in the background.
If you'd use this annotation, you'd specify your main Spring Boot class here (your entry-point class with the #SpringBootApplication annotation).
From what I can see in your test and your question is that you want to provide an actual bean for the TravelOutputDtoMapper, but mock the TravelService.
In this case, you can use #TestConfiguration to add further beans to your sliced Spring TestContext:
// #ExtendWith(SpringExtension.class) can be removed. This extension is already registered with #WebMvcTest
#WebMvcTest(controllers = TravelController.class)
class TravelControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private TravelService travelService;
#Autowired
private TravelOutputDtoMapper travelOutputDtoMapper;
#TestConfiguration
static class TestConfig {
#Bean
public TravelOutputDtoMapper travelOutputDtoMapper() {
return new TravelOutputDtoMapper(); // I assume your mapper has no collaborators
}
}
// ... your MockMvc tests
}

Values not passed into Component from application-dev.properties

I am writing tests in Junit and am using Spring Boot Framework (which I am new to) and need to use different urls to test different environments
Hence, I created 2 resource files in addition to application.properties
1> application-dev.properties
2> application-stage.properties
I created a component which I set the property values to be read into.
Lastly in my test file I am annotating the Test Class with:
#ContextConfiguration(classes = {ComponentName.class})
Also in my application.properties I have this line:
spring.profiles.active=dev
Expected:
When I print out the value of the property of the Component class it should take the value from application-dev.properties
Actual: the value is null, although my bean is successfully created
Why is the property not getting injected with the value from application-dev.properties?
I've tried several articles from Baeldung (A bit confusing though, the articles demonstrate multiple methods to do the same thing, but does not show 1 full technique to do everything end-to-end
https://www.baeldung.com/spring-profiles
I also tried with setting the active profile multiple ways:
a. Env variable
b. Using #ActiveProfiles annotation
Note: This is a Test Project (I am trying to automate-test a website, so all my resource files are inside src.test.resources
application.properties
#spring
https://ops.dev.website.us
spring.profiles.active=dev
url.sms=https://ops.default.website.us
application-dev.properties
url.sms=https://ops.dev.website.us
application-stage.properties
url.sms=https://ops.stage.website.us
Component File
#Component
#ConfigurationProperties(prefix = "url")
public class DevEnvironment {
private String sms;
public String getSms() {
return sms;
}
public void setSms(String sms) {
this.sms = sms;
}
}
Test File
#EnableAutoConfiguration(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {DevEnvironment.class})
public class MyTest implements ApplicationContextAware {
#Autowired
private ConfigurableEnvironment env;
private DevEnvironment devEnvironment;
String url;
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
System.out.println("Active profiles:" + env.getActiveProfiles()[0]);
devEnvironment = context.getBean(DevEnvironment.class);
//System.out.println("from set app context:" + devEnvironment.getSms());
url = devEnvironment.getSms();
}
#Test
public void testSms(){
System.out.println("inside test url:" +url);
}
}
Expected: When I print out the value of the property of the Component class it should take the value from application-dev.properties
Actual: the value is null, although my bean is successfully created
You forgot #SpringBootTest annotation to MyTest class.
#EnableAutoConfiguration(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
#RunWith(SpringRunner.class)
#SpringBootTest //add
#ContextConfiguration(classes = {DevEnvironment.class})
public class MyTest implements ApplicationContextAware {
//
}
I worked fine with this.
reference:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications

Spring Boot - Test for controller fails with 404 code

I want to write a test for controller. Here is test snippet:
#RunWith(SpringRunner.class)
#WebMvcTest(WeatherStationController.class)
#ContextConfiguration(classes = MockConfig.class)
public class WeatherStationControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private IStationRepository stationRepository;
#Test
public void shouldReturnCorrectStation() throws Exception {
mockMvc.perform(get("/stations")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
controller code snippet:
#RestController
#RequestMapping(value = "stations")
public class WeatherStationController {
#Autowired
private WeatherStationService weatherService;
#RequestMapping(method = RequestMethod.GET)
public List<WeatherStation> getAllWeatherStations() {
return weatherService.getAllStations();
}
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public WeatherStation getWeatherStation(#PathVariable String id) {
return weatherService.getStation(id);
}
MockConfig class:
#Configuration
#ComponentScan(basePackages = "edu.lelyak.repository")
public class MockConfig {
//**************************** MOCK BEANS ******************************
#Bean
#Primary
public WeatherStationService weatherServiceMock() {
WeatherStationService mock = Mockito.mock(WeatherStationService.class);
return mock;
}
Here is error stack trace:
java.lang.AssertionError: Status
Expected :200
Actual :404
I can get what is wrong here.
How to fix test for controller?
HTTP code 404, means no resource found (on the server) for your request, which I think that your controller is not visible(let me say is not scanned) by spring boot.
A simple solution is scanning a parent package in MockConfig class, so spring can pick up all beans,
#ComponentScan(basePackages = "edu.lelyak") // assuming that's the parent package in your project
if you don't like this approach, you can add the controller's package name in basePackages
#ComponentScan(basePackages = {"edu.lelyak.controller","edu.lelyak.repository")
BTW, you don't have to manually set up WeatherStationService in MockConfig class, Spring boot can inject a mock for you and automatically reset it after each test method, you should just declare it in your test class:
#MockBean
private IStationRepository stationRepository;
On the other hand, you should mock weatherService.getAllStations() before calling get("/stations") in your test method (as you're not running integration test), so you can do:
List<WeatherStation> myList = ...;
//Add element(s) to your list
Mockito.when(stationService.getAllStations()).thenReturn(myList);
You can find more in :
Testing improvements in Spring Boot 1.4
Spring Boot features: Testing
I had the same issue. The controller was not getting picked up despite specifying it with #WebMvcTest(MyController.class). This meant all of its mappings were ignored, causing the 404. Adding #Import(MyController.class) resolved the issue, but I didn't expect the import to be necessary when I'm already specifying which controller to test.
I am not sure why your test is not working. But I got another solution which works for me.
#SpringBootTest
public class ControllerTest {
#Autowired
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(new TestController()).build();
}
#Test
public void shouldReturnCorrectStation() throws Exception {
mockMvc.perform(get("/stations")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
After some debugging, it appears that the target controller is simply not registered as a method handler. Spring scans the beans for the presence of RestController annotation.
But the problem is that the annotation could be found only if the bean is proxied via CGLIB, but for the case when we use WebMvcTest it's proxied by JDK.
As a result, I searched for the configuration which is responsible for making the choice, and the one was finally found AopAutoConfiguration. So when SpringBootTest is used this one is autoloaded when you need WebMvcTest+PreAuthorize in your controllers, then simply use:
#Import(AopAutoConfiguration.class)
I import external configuration class by #ContextConfiguration(classes = MyConfig.class)
When I changed in MyConfig annotation #Configuration into #TestConfiguration it started to work properly.
I couldn't find a good answer but I could find one of the causes.
I was using in my tests the #PreAuthorize on the RestController.
You can mock the Oauth with this tip on the integration tests that use SpringBootTest. For SpringBootTest, this works very well too, but using SpringBootTest you load a lot of other resources (like JPA) that is not necessary to do a simple Controller test.
But with #WebMvcTest this not works as expected. The use of the WithMockOAuth2Scope annotation can be enough to stop the 401 error from authentication problem, but after that the WebMvcTest can't find the rest endpoint, returning the 404 error code.
After removing the #PreAuthorize on Controller, the test with WebMvcTest pass.
Based on the accepted answer, in my case I had copied and modified the file based on another test, but forgot to change the name for the controller on the top of the class, that being the reason why it was not finding the resource, as the error says.
#RunWith(SpringRunner.class)
#WebMvcTest(AnswerCommandController.class)
public class AnswerCommandControllerTest {
Here is a different approach to the controller test that worked for me.
Assumption: The class WeatherStationService is a #SpringBootApplication
Then, the test class below should work for you:
#RunWith(SpringRunner.class)
#SpringApplicationConfiguration(WeatherStationService.class)
#WebIntegrationTest
public class WeatherStationControllerTest {
#Autowired
private WebApplicationContext context;
MockMvc mockMvc;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
#Test
public void shouldReturnCorrectStation() throws Exception {
mockMvc.perform(get("/stations")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk();
}
}
With this test setup, you should no longer need the MockConfig class.
In my case it was about a missing starting slash /
I've appended / to both RequestMapping value and MockHttpServletRequestBuilder post urlTemplate parameters as first character.
In case anyone is wondering.
If we don't use #ContextConfiguration, #WebMvcTest annotation will load the REST controller class. Otherwise, when we use use #ContextConfiguration, seems ContextConfiguration clear the context REST controller config. We need to add the REST controller to ContextConfiguration such as:
#ContextConfiguration(classes = {MockConfig.class, WeatherStationController.class})

ContentType not set in Mockito test

I am trying to get a unit test to work with Mockito and Spring MVC on a RESTful GET controller. Here is my test:
#RunWith(MockitoJUnitRunner.class)
#WebAppConfiguration
#ContextConfiguration(locations = {"/test-context.xml","/dataaccess-context.xml"})
public class FormControllerTest {
private MockMvc mockMvc;
#Autowired
FormImplBean formBean;
#Mock
private FormService formServiceMock;
#InjectMocks
private FormController formController;
#Before
public void setup() {
// Process mock annotations
MockitoAnnotations.initMocks(this);
// Setup Spring test in standalone mode
this.mockMvc = MockMvcBuilders.standaloneSetup(formController).build();
}
#Test
public void testGet() throws Exception {
when(formServiceMock.getFormImplById(1)).thenReturn(formBean);
mockMvc.perform(get("/Form/form/{id}", 1))
.andExpect(status().is2xxSuccessful())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
verify(formServiceMock, times(1)).getFormImplById(1);
verifyZeroInteractions(formServiceMock);
}
}
And here is my controller method:
#RequestMapping(value = "/form/{formId}", method = RequestMethod.GET)
#ResponseBody
public FormImplBean getForm(#PathVariable("formId") int formId ) {
return formService.getFormImplById(formId);
}
I keep getting:
java.lang.AssertionError: Content type not set
Of course when I go an look at the real controller on the server, using firefox developer tools, I see that the content type is set correctly.
I tried adding the produces="application/json" to the controller but that did not work, (nor do I think I should have to right?)
Without the content type check, the test passes fine.
I am using:
Spring 4.2.7 -
Mockito 1.10.19 -
Jackson 2.7.0 -
Junit 4.12
in a maven build
Any Ideas?

Spring Boot Test : Property source with #Value not resolved

I've a problem in my JUNIT test with Spring boot : the #Value is not resolved.
Here the code :
Spring boot config class :
#Configuration
#PropertySource(value="classpath:/config/parametrage-environnement.properties",name="env")
public class ExternalRessourceConfiguration {
//To resolve ${} in #Value
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Class test :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#IntegrationTest
public class ConnexionEbicsResourceIntTest {
#Test
#Transactional
public void createConnexionEbics() throws Exception {
restConnexionEbicsMockMvc.perform(post("/api/connexionEbicss")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(connexionEbicsDTO)))
.andExpect(status().isCreated());
Java ressource :
#RestController
#RequestMapping("/api")
public class ConnexionEbicsResource {
#Value("${env['connexion.proxy.host']}")
//#Value("${connexion.proxy.host}")
public String protocol;
#RequestMapping(value = "/connexionEbicss",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public ResponseEntity<ConnexionEbicsDTO> createConnexionEbics( #RequestBody ConnexionEbicsDTO connexionEbicsDTO) throws URISyntaxException {
log.debug("REST request to save ConnexionEbics : {}", connexionEbicsDTO);
String a = protocol;
}
In java ressource, when I run the test, "a" is null. The #Value was not resolved, why ? My spring boot configuration was ALL bootstraped.
The parametrage-environnement.properties file is located in both paths : src/main/resources/config and src/test/resources/config (copy/paste)
1) Since my controller is mocked, I can't use spring injection directly present in the controller (mocking annihilate Spring !)
2) Syntax
#Value("${env['connexion.proxy.host']}")
is wrong because, env is supposed to be a spring bean (#Bean). See here
So, With #PropertySource, we have to use #Value("${connexion.proxy.host}") syntax and don't forget the Resolver:
//To resolve ${} in #Value
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
3) To set the protocol in the controller class, I need to inject it from the Test class :
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
ConnexionEbicsResource connexionEbicsResource = new ConnexionEbicsResource();
ReflectionTestUtils.setField(connexionEbicsResource, "connexionEbicsService", connexionEbicsService);
**ReflectionTestUtils.setField(connexionEbicsResource, "protocol", protocol);**
ReflectionTestUtils.setField(connexionEbicsResource, "connexionEbicsMapper", connexionEbicsMapper);
this.restConnexionEbicsMockMvc = MockMvcBuilders.standaloneSetup(connexionEbicsResource)
.setCustomArgumentResolvers(pageableArgumentResolver)
.setMessageConverters(jacksonMessageConverter).build();
}
Having the loaded resource bundle in the test class :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#IntegrationTest
#PropertySource(value="classpath:/config/parametrage-environnement.properties",name="env")
//#PropertySource("classpath:/config/parametrage-environnement.properties")
public class ConnexionEbicsResourceIntTest {
....
#Value("${connexion.proxy.host}")
public String protocol;
Thanks anyway

Resources