how to test rest controller with custom spring DispatcherServlet - spring

I'm trying to test my controller and I want to my custom DispatcherServlet include on that.
my dispatcher is something like this:
public class LoggableDispatcherServlet extends DispatcherServlet {
private static final Logger LOGGER = LoggerFactory.getLogger(LoggableDispatcherServlet.class);
#Autowired
private LogsService logsService;
#Override
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws
Exception
{
.
.
.
mapper.readTree(requestWrapper.getContentAsByteArray());
int status = responseWrapper.getStatus();
JsonNode newNode = mapper.readTree(responseWrapper.getContentAsByteArray());
JsonNode responseJson= newNode;
logsService.addLogs(request,response ,...)
}
}
and my test Class is something like this:
#SpringBootTest
#RunWith(SpringRunner.class)
public class RestControllerTest {
private final Resource test1 = new ClassPathResource("test1.json");
private MockMvc mockMvc;
#Autowired
private RestTemplate restTemplate;
#Autowired
private WebApplicationContext wac;
#BeforeEach
public void before() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
this.mockRestServiceServer = MockRestServiceServer.bindTo(this.restTemplate)
.build()
}
#Test
public void getAvailableLoanTest() throws Exception{
String url1="http://localhost:80801/testService?param1=1&param2=2";
this.mockRestServiceServer
.expect(ExpectedCount.manyTimes(), requestTo(url1))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(this.test1, MediaType.APPLICATION_JSON_UTF8));
this.mockMvc.perform(post("/api/myApi")
.contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(new MyRequest(1, 2,))))
.andExpect(status().is2xxSuccessful())
.andExpect(content().contentType("application/json"))
//.andExpect(jsonPath("#.res1").value(1L))
.andReturn();
.
.
.
}
}
The problem is my request receive directly by rest controller and I can't test the DispatcherServlet.
I'm using spring boot 2.2.3 with jar packaging and I don't have web.xml or any servlet configration.

Related

Spring boot test, #WithMockUser produce NPE in Controller method

i have an issue with my controller test
So my base test class configured like so
#SpringBootTest
#WithMockUser(username = "test_user",authorities = "Configured_allacess_authority")
public abstract class BaseControllerTest extends DatabaseIT {
protected static long counter;
protected MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
protected ObjectMapper objectMapper;
protected MockRestServiceServer restServiceServer;
#Autowired
RestTemplate restTemplate;
#BeforeEach
protected void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.build();
restServiceServer = MockRestServiceServer.createServer(restTemplate);
}
and test code
mockMvc.perform(request)
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(content().json(s));
When i debugging my controller method which is
public ResponseEntity<?> getSomething (
#Parameter(description = "ID") final String id,
#ApiIgnore #AuthenticationPrincipal Authentication user){
////
}
user value is null, though when i invoke SecurityContextHolder.getContext().getAuthentication() here in controller i'm getting Authentication object that's refers to one i mocked in
#WithMockUser(username = "test_user",authorities = "Configured_allacess_authority")
So recently i've updated my project to Java 17 , that also forced me to update spring boot to
version: '2.5.5'
and spring cloud to
"org.springframework.cloud:spring-cloud-dependencies:2020.0.5"

Using #RestClientTest in spring boot test

I want to write a simple test using #RestClientTest for the component below (NOTE: I can do it without using #RestClientTest and mocking dependent beans which works fine.).
#Slf4j
#Component
#RequiredArgsConstructor
public class NotificationSender {
private final ApplicationSettings settings;
private final RestTemplate restTemplate;
public ResponseEntity<String> sendNotification(UserNotification userNotification)
throws URISyntaxException {
// Some modifications to request message as required
return restTemplate.exchange(new RequestEntity<>(userNotification, HttpMethod.POST, new URI(settings.getNotificationUrl())), String.class);
}
}
And the test;
#RunWith(SpringRunner.class)
#RestClientTest(NotificationSender.class)
#ActiveProfiles("local-test")
public class NotificationSenderTest {
#MockBean
private ApplicationSettings settings;
#Autowired
private MockRestServiceServer server;
#Autowired
private NotificationSender messageSender;
#Test
public void testSendNotification() throws Exception {
String url = "/test/notification";
UserNotification userNotification = buildDummyUserNotification();
when(settings.getNotificationUrl()).thenReturn(url);
this.server.expect(requestTo(url)).andRespond(withSuccess());
ResponseEntity<String> response = messageSender.sendNotification(userNotification );
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
private UserNotification buildDummyUserNotification() {
// Build and return a sample message
}
}
But i get error that No qualifying bean of type 'org.springframework.web.client.RestTemplate' available. Which is right of course as i havn't mocked it or used #ContextConfiguration to load it.
Isn't #RestClientTest configures a RestTemplate? or i have understood it wrong?
Found it! Since i was using a bean that has a RestTemplate injected directly, we have to add #AutoConfigureWebClient(registerRestTemplate = true) to the test which solves this.
This was in the javadoc of #RestClientTest which i seem to have ignored previously.
Test which succeeds;
#RunWith(SpringRunner.class)
#RestClientTest(NotificationSender.class)
#ActiveProfiles("local-test")
#AutoConfigureWebClient(registerRestTemplate = true)
public class NotificationSenderTest {
#MockBean
private ApplicationSettings settings;
#Autowired
private MockRestServiceServer server;
#Autowired
private NotificationSender messageSender;
#Test
public void testSendNotification() throws Exception {
String url = "/test/notification";
UserNotification userNotification = buildDummyUserNotification();
when(settings.getNotificationUrl()).thenReturn(url);
this.server.expect(requestTo(url)).andRespond(withSuccess());
ResponseEntity<String> response = messageSender.sendNotification(userNotification );
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
}
private UserNotification buildDummyUserNotification() {
// Build and return a sample message
}
}

JUnit not working in Spring Boot with #Autowired annotation

I have a Spring Boot application in which I'm creating REST web services
using the MVC pattern.
I have a controller, service and DAO class and I use the #Autowired annotation for calling methods of the service and DAO layer.
When I create JUnit tests using mockito, the values are going into the controller but from the controller they are not going to the service class.
Here is the code sample:
#WebAppConfiguration
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {AppplicationConfiguration.class})
public class ExternalControllerTest {
private MockMvc mockMvc;
#InjectMocks
private MyController myController;
#MockBean
myService myService;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.standaloneSetup(myController)
.build();
}
#Test
public void testListCluster() throws Exception {
Input emrInput = new Input();
emrInput.setId("200004773");
emrInput.setName("test");
String expected = "{\"status\":\"Success\",\"message\":\"Success\",\"data\":\"somevalue\"}";
AutomateRestResponse response = new AutomateRestResponse<JsonObject>();
response.setMessage("Success");
response.setStatus("Success");
response.setData("somevalue");
Mockito.when(
externalService.listCluster(emrInput)
).thenReturn(response);
mockMvc.perform(post("/v1/gerData"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status", is("Success")));
verify(externalService, times(1)).listCluster(emrInput);
RequestBuilder requestBuilder = MockMvcRequestBuilders
.post("/v4/listCluster")
.accept(MediaType.APPLICATION_JSON).content(emrInputJosn)
.contentType(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
System.out.println("response body1" + result.getResponse()
.getContentAsString());
}`
Please help me with this.
It is not clear from your question what you are trying to mock.
Anyway, you should not be able to debug your service/dao that is mocked since what actually executed in the test is the mocked one and not yours.
If you want to test your controller, you can mock your service or dao and define what the response they will return, and then verify that the response you get from your controller is as you expect it to be.
#EnableWebMvc
#SpringBootApplication(scanBasePackages = { "com.yourPackage.external" })
public class YourApplication extends org.springframework.boot.web.support.SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(applicationClass, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(applicationClass);
}
private static Class<Application> applicationClass = Application.class;
}
Based on what you have pasted, you can do below things:
if you are using #RunWith(SpringJUnit4ClassRunner.class) [better change to #RunWith(SpringRunner.class)] then use
#MockBean
private MyService externalService;
OR
use #RunWith(MockitoJUnitRunner.class) and
#MockBean
private MyService externalService;
#InjectMocks
private MyController controller = new MyController(externalService);
for details checkout :- testing web in spring boot
#WebAppConfiguration
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {AppplicationConfiguration.class})
public class ExternalControllerTest {
private MockMvc mockMvc;
#InjectMocks
private MyController myController ;
#MockBean
myService myService;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.standaloneSetup(myController)
.build();
}
#Test
public void testListCluster() throws Exception {
Input emrInput = new Input();
emrInput.setId("200004773");
emrInput.setName("test");
String expected = "{\"status\":\"Success\",\"message\":\"Success\",\"data\":\"somevalue\"}";
AutomateRestResponse response = new AutomateRestResponse<JsonObject>();
response.setMessage("Success");
response.setStatus("Success");
response.setData("somevalue");
Mockito.when(
externalService.listCluster(emrInput)
).thenReturn(response);
mockMvc.perform(post("/v1/gerData"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status", is("Success")));
verify(externalService, times(1)).listCluster(emrInput);
RequestBuilder requestBuilder = MockMvcRequestBuilders
.post("/v4/listCluster")
.accept(MediaType.APPLICATION_JSON).content(emrInputJosn)
.contentType(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
System.out.println("response body1"+ result.getResponse()
.getContentAsString());
}
}

Spring test invalid configuration

I have a simple REST controller that I'm trying to test, the test looks like this:
#RunWith(SpringRunner.class)
#SpringBootTest
#WebAppConfiguration
public class ModelControllerTests {
private MediaType contentType = MediaType.APPLICATION_JSON_UTF8;
private HttpMessageConverter jacksonConverter;
private MockMvc mockMvc;
#Mock
private ModelService service;
#InjectMocks
private ModelController controller;
#Autowired
private WebApplicationContext context;
#Autowired
void setConverters(HttpMessageConverter<?>[] converters) {
jacksonConverter = Arrays.stream(converters)
.filter(hmc -> hmc instanceof MappingJackson2HttpMessageConverter)
.findAny()
.orElse(null);
assertNotNull(jacksonConverter);
}
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mockMvc = standaloneSetup(controller)
.build();
}
#Test
public void postModel_returnsModel() throws Exception {
when(service.doSomething(any())).thenReturn(new Model("cde", null));
mockMvc.perform(post("/model")
.content(json(new Model("abc", null)))
.contentType(contentType))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().json("{\"notEmpty\":\"abc\"}", true));
}
private String json(Object o) throws IOException {
var responseMessage = new MockHttpOutputMessage();
jacksonConverter.write(o, MediaType.APPLICATION_JSON_UTF8, responseMessage);
return responseMessage.getBodyAsString();
}
}
Now I've got a problem with dependencies and configuration, I've got the following line in my application.properties: spring.jackson.default-property-inclusion=non_null, which works fine when using the normal mockMvc (webAppContextSetup), however I wanted to mock ModelService (which is autowired in ModelController.
When using standaloneSetup to create MockMvc instance there seems to be no configuration, fields that are set to null are returned and furthermore it seems that the ModelService annotated with #Mock is not the same as the one in ModelController, therefore when postModel_returnsModel it's using the wrong service.
How can I solve this?

Is it possible to use MockMvc and mock only the RestTemplate used by my service?

I'm trying to build one integration test using MockMvc and I want to mock only the RestTemplate used by MyService.java. If I uncomment the code on MyIT.java, this test will fail because the RestTemplate used by MockMvc will be mocked as well.
MyRest.java
#RestController
public class MyRest {
#Autowired
private MyService myService;
#RequestMapping(value = "go", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<String> go() throws IOException {
myService.go();
return new ResponseEntity<>("", HttpStatus.OK);
}
}
MyService.java
#Service
public class MyService {
#Autowired
private RestTemplate restTemplate;
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
public void go() {
restTemplate.getForObject("http://md5.jsontest.com/?text=a", String.class);
}
}
MyIT.java
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class)
#AutoConfigureMockMvc
//#RestClientTest(MyService.class)
public class MyIT {
#Autowired
private MockMvc mockMvc;
// #Autowired
// private MockRestServiceServer mockRestServiceServer;
#Test
public void shouldGo() throws Exception {
// mockRestServiceServer.expect(requestTo("http://md5.jsontest.com/?text=a"))
// .andRespond(withSuccess());
mockMvc.perform(get("/go")).andExpect(status().isOk());
}
}
First, you should #Autowired your RestTemplate bean to your test
class.
Then create the MockRestServiceServer with the restTemplate, instead of
autowiring it.
Perhaps try this one:
#Autowired
private RestTemplate restTemplate;
private MockRestServiceServer mockRestServiceServer;
#Before
public void setup() {
mockRestServiceServer= MockRestServiceServer.createServer(restTemplate);
}

Resources