Preperate data with MockMVC for GET method - java

I'am trying to test my get all events method. So i'm trying to first fill one event into database and then perform get method by MockMVC. But it is returning empty content.
I have tried to change specific annotations such like MockBean/Autowired on eventService. Also tried to change annotations on test class. Event controller has these 3 annotations:
#RequestMapping("/event")
#CrossOrigin
#RestController
Method to add event
public void addEvent(final Event event)
{
eventRepository.save(event);
}
Method to get events
public Iterable<Event> getAll()
{
return eventRepository.findAll();
}
Test for get method
#SpringBootTest
#AutoConfigureMockMvc
class EventControllerTest extends Specification {
#MockBean
private EventService eventService
#Autowired
private MockMvc mockMvc
def "whet get is performed on all endpoint the response has status 200"()
{
given:
eventService.addEvent(new Event(new Date(), "type", "league", "team", "msg"))
expect: "the status is 200"
when:
ResultActions resultActions = mockMvc.perform(get("/event/all"))
I expect to get method return one event but actual it returns 0.

Related

Spring #ControllerAdvice not applied when packageName or assignableTypes is specified

A sample project demonstrating the issue can be found at: https://github.com/dirkbolte/javaexceptionhandlertest
I have a controller which is declared to produce JSON content:
#RestController
public class TestController {
#GetMapping(value = "exception", produces = MediaType.APPLICATION_JSON_VALUE)
public void test() {
}
}
The corresponding controller advice should map any exception to a BAD_REQUEST :
#RestControllerAdvice
public class MyExceptionHandler {
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(value = Exception.class)
public void exceptionhandler() {
}
}
I test that a call with a wrong accept header results in the correct error response code (as procudes specifies something different):
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
class TestControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void test() throws Exception {
mockMvc.perform(
MockMvcRequestBuilders.get("/exception")
.accept(MediaType.APPLICATION_PDF)
).andExpect(status().isBadRequest());
}
}
The code above "fails successfully" :-) . In my production code I want to limit the controller advice to a certain controller (alternatively to a certain package), thus I specified assignableTypes on #ControllerAdvice: #RestControllerAdvice(assignableTypes = TestController.class) . Now the test fails as the exception handler isn't called anymore. Same happens when specifying basePackages or basePackageClasses - or when using an interface.
The version on GitHub shows the test failure, so I hope it is easy to reproduce.
How can I use a ControllerAdvice explicitly for one or more Controller?
The requested URL will not get mapped to any Controller method here. This would result in a Status 406 error (due to the specified accept type). Since no exception is thrown from the Controller methods the ControllerAdvice would not be invoked.
On dispatcher level, org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#getExceptionHandlerMethod will be called with handlerMethod = null which is why only generic controller advices are applied.
The ControllerAdvice would work here, if the request contains additional metadata that the controller method is configured for and/or an exception is thrown from within that method.

How to unit test a cachable method

I am using Spring Cache.
I have a Spring controller and as part of the method responsible for that GET request, I have annotated it with #Cacheable(value = "customer", key = "#accountId"). In that method, it calls an API and does some business logic and then returns a DTO. With the cache annotation in place, I'm expecting on the first execution of this code will run normally but any subsequent calls later, it'll fetch the result from the cache.. that's correct right?
I'm writing a unit test to verify that the API was called once despite mocking multiple requests being sent to that controller. But the problem is that it keeps on calling the API multiple times and not calling the cache.
Unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#WebMvcTest(controllers = CustomerController.class, secure = false)
public class CustomerControllerTest {
private static final String ACCOUNT_ID = "1111";
#MockBean
private CustomerService customerService;
#MockBean
private CustomerPortAPI customerPortAPI;
#Autowired
MockMvc mockMvc;
#Before
public void setUp(){
when(customerService.getStatus(any())).thenReturn("test");
when(customerPortAPI.getAccount(any())).thenReturn(Account.builder().build());
}
#Test
public void shouldReturnCustomerDTOt() throws Exception {
when(customerService.Status(any())).thenReturn("test");
when(customerPortAPI.getAccount(ACCOUNT_ID)).thenReturn(Account.builder().accountId(ACCOUNT_ID).build());
mockMvc.perform(get("/customer/{accountId}/status", ACCOUNT_ID)
.accept(APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("status").value(customer.NOT_REQUIRED.name()));
mockMvc.perform(get("/customer/{accountId}/status", ACCOUNT_ID)
.accept(APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("status").value(customer.NOT_REQUIRED.name()));
verify(customerPorAPI, times(1)).getAccount(ACCOUNT_ID);
}}
Controller Method:
#Cacheable(value = "statusEligibility", key = "#customerId")
#GetMapping
public CustomerStatusDTO getCustomerStatus(#PathVariable String customerId) {
Customer customer = cusomterPort.getAccount(customerId);
Status status = service.getStatus(customer);
if (status.equals(Cons.REQUIRED)) {
/.../
} else {
/.../
}

How to make a async REST with Spring?

I'm trying to make a small REST using Spring Boot.
I've never used Spring and used Java a long time ago (Java 7)!
In the last 2 years I have used only Python and C# (but like I said, I already used Java).
So, now, I'm trying to make a REST using async methods, and checked several examples, but still, I don't understand very well the "correct way" to do this.
Looking at the following documentation: http://carlmartensen.com/completablefuture-deferredresult-async, Java 8 has CompletableFuture that I can use with Spring, so, I made the following code:
Service:
#Service
public class UserService {
private UserRepository userRepository;
// dependency injection
// don't need Autowire here
// https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Async
public CompletableFuture<User> findByEmail(String email) throws InterrupedException {
User user = userRepository.findByEmail(email);
return CompletableFuture.completedFuture(user);
}
}
Repository:
public interface UserRepository extends MongoRepository<User, String> {
#Async
findByEmail(String email);
}
RestController:
#RestController
public class TestController {
private UserService userService;
public TestController(UserService userService) {
this.userService = userService;
}
#RequestMapping(value = "test")
public #ResponseBody CompletableFuture<User> test(#RequestParam(value = "email", required = true) String email) throws InterruptedException {
return userService.findByEmail(email).thenApplyAsync(user -> {
return user;
})
}
}
This code give me the expected output.
Then, looking at another documentation (sorry, I lost the link), I see that Spring accept the following code (which give me the expected output too):
#RequestMapping(value = "test")
public #ResponseBody CompletableFuture<User> test(#RequestParam(value = "email", required = true) String email) throws InterruptedException {
return userService.findByEmail(email);
}
}
Is there a difference between the two methods?
Then, looking at the following guide: https://spring.io/guides/gs/async-method/, there's a #EnableAsync annotation in SpringBootApplication class.
If I include the #EnableAsync annotation and create a asyncExecutor Bean like the code from last link, my application don't return nothing on /test endpoint (only a 200 OK response, but with blank body).
So, my rest is async without the #EnableAsync annotation?
And why when I use #EnableAsync, the response body is blank?
The response body is blank because the #Async annotation is used at findEmail method of UserRepository class, it means that there is no data returned to the following sentence User user = userRepository.findByEmail(email); because findByEmail method is running on other different thread and will return null instead of a List object.
The #Async annotation is enabled when you declare #EnableAsync that is the reason why it only happens when you use #EnableAsync because it activates the #Async of findEmail method to run it on other thread.
The method return userService.findByEmail(email); will return a CompletableFuture object that is created from UserService class.
The difference with the second method call is that thenApplyAsync method will create a totally new CompletableFuture from the previous one that comes from userService.findByEmail(email) and will only return the user object that comes from the first CompletableFuture.
return userService.findByEmail(email).thenApplyAsync(user -> {
return user;
})
If you want to get the expected results just remove the #Async annotation from findByEmail method, and finally add the #EnableAsync Annotation
If you need to clarify ideas of how to use Async methods, lets say that you have to call three methods and each one takes 2 seconds to finish, in a normal scenario you will call them method1, then method2 and finally method3 in that case you entire request will take 6 seconds. When you activate the Async approach then you can call three of them and just wait for 2 seconds instead of 6.
Add this long method to user service:
#Async
public CompletableFuture<Boolean> veryLongMethod() {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return CompletableFuture.completedFuture(true);
}
And call it three times from Controller, like this
#RequestMapping(value = "test")
public #ResponseBody CompletableFuture<User> test(#RequestParam(value = "email", required = true) String email) throws InterruptedException {
CompletableFuture<Boolean> boolean1= siteService.veryLongMethod();
CompletableFuture<Boolean> boolean2= siteService.veryLongMethod();
CompletableFuture<Boolean> boolean3= siteService.veryLongMethod();
CompletableFuture.allOf(boolean1,boolean2,boolean3).join();
return userService.findByEmail(email);
}
Finally measure the time that takes your response, if it takes more than 6 seconds then you are not running Async method, if it takes only 2 seconds then you succeed.
Also see the following documentation: #Async Annotation, Spring async methods, CompletableFuture class
Hope it help.
The Asynchronous child threads start executing very late (around 20 to 30 seconds delay).
I'm using ThreadPoolTaskExecutor() in my main SpringBoot application class. You can also try the same if you consider performance as a factor.

Is there a way to verify that a method on Spring Controller was called using Mockito

This is my Mockito Test:
#RunWith(MockitoJUnitRunner.class)
public class Controller_Test {
private MockMvc mockMvc;
#InjectMocks
private EmployeeController employeeController;
#Mock
private InputValidationService inputValidationService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(restController).build();
}
#Test
public void testGetEmployeeDetails() {
EmployeeController spy = Mockito.spy(employeeController);
MvcResult result = mockMvc.perform(get("/employee/details/9816")).andDo(print()).andExpect(status().isOk()).andReturn();
// Have some basic asserts here on the result that are working fine
}
}
Now my question is, Is there a way to assert that the method I expected to be called in my controller was actually invoked.
I know it was, but how do I assert it with unit test
For e.g.
This is my RequestMapping in the controller:
#RequestMapping(value = "/employee/details/{id}", produces = "application/json; charset=UTF-8", method = RequestMethod.GET)
#ResponseStatus( HttpStatus.OK)
#ResponseBody
public EmployeeDetails getEmployeeDetailsById(#PathVariable String employeeID) {
//Some business logic
}
Now I would like to make some assertion like:
Mockito.verify(spy, times(1)).getEmployeeDetailsById();
So basically I would like to assert that the method I expected to get called was the one that got called. I know this can be done on the Mock Service object that I have i.e. inputValidationService but would like something similar for the controller as well.
Please let me know if there are any additional details that you would like me to post.
Maybe late, but I found org.springframework.test.web.servlet.result.HandlerResultMatchers which can verify the proper controller and method are being called. For example:
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.handler;
mockMvc
.perform(get("/employee/details/9816"))
.andExpect(handler().handlerType(EmployeeController.class))
.andExpect(handler().methodName("getEmployeeDetailsById"));
You can use #MockBean.This will use Mockito beans for which you will be able to use standart Mockito functions like "verify".
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing-spring-boot-applications-mocking-beans

A spring integration test

In my integration test, I tried to use resttemplate to send a Get request to a dummy server created by MockMvcBuilders. However I got an error:
I/O error on GET request for "http://localhost:8080/test":Connection refused:
(In the function testAccess(), url is "http://localhost:8080/test"). My code is as below:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#IntegrationTest("server.port=8080")
public class MyTest {
private MockMvc mockMvc = null;
#Autowired
private WebApplicationContext context;
#Value("${server.port}")
private int port;
#Autowired
private MyController myController;
#Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.build();
}
#Test
public void testAccess() throws Exception{
RestTemplate restTemplate=new RestTemplate();
String url="http://localhost:8080/test";
try{
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
}
catch(ResourceAccessException ex){
System.out.println(ex.getMessage());
}
}
#Controller
public static class MyController {
#RequestMapping(value = "/test", method = RequestMethod.GET)
public #ResponseBody String access() {
return "OK";
}
}
}
The way I've done it is this:
First, you create a mock server from the actual RestTemplate you are using in your application
#Before
public void setup() throws Exception {
mockServer = MockRestServiceServer.createServer(myService.restTemplate);
}
Then you define how that request is going to work:
mockServer.expect(requestTo("http://localhost:8080/myrestapi"))
.andExpect(method(HttpMethod.POST))
.andRespond(withSuccess("{ success: true }", MediaType.APPLICATION_JSON));
And last you call the method in your application that will trigger a call to that url with that RestTemplate:
#Test
public void testThis() throws Exception {
myService.somethingThatCallsMyRestApi(parameters);
}
That will make your tests work as if there was a server up and running to process requests.
Using this in your example makes no sense, cause you would be testing that you build your test correctly and nothing else from the actual application.
The problem with this is that you cannot test dynamic responses. I mean, in my case the method I'm calling generates different data every time you call it and then sends it to the mockServer and then validates that the response matches in some very specific way. I haven't found a solution yet, but if the data you are going to send and receive is previously known, which would be in most cases, you'll have no problem using this.
Why are you defining a controller in your Test class and then trying to test it ? It doesn't feel logical to try to test something that is defined within the test it self.
Rather you would want to test a controller defined somewhere outside your tests, an actual controller that is used within your application.
Let's say MyController is defined as an actual controller then you could use the mockMvc object you created to test it.
mockMvc.perform(get('/test'))
.andExpect(status().isOk())

Categories