How to mock user defined Annotation with Mockito - spring-boot

I'm writing test cases for my controller layer. One Of my controller method looks like below.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#RestController
#RequestMapping("/v1")
public class MyController {
private static Logger logger = LoggerFactory.getLogger(MyController.class);
#Autowired
private MyService service;
#GetMapping("/{pathVar}/check")
#MyAuthentication
public ResponseEntity<Object> check(#PathVariable String pathVar,
#MyAuthContext MyAuthInfo myAuthInfo) throws Exception {
try {
logger.info("Check API started by "+myAuthInfo.getUserId()+" and email "+ myAuthInfo.getUserEmail());
return ResponseEntity
.status(HttpStatus.OK)
.body(service.isPartner(pathVar, myAuthInfo));
} catch (Exception e) {
logger.info("Check API encountered the error.", e);
throw e;
}
}
}
Can someone please help to mock my auth context while I'm writing test cases?

Related

Junit Test for Controller with parameters

Hi I'm trying to write Junit test for a controller but cant seem to find the proper approach can anyone please suggest how could write a junit test case for the following controller:
Contoller Class
public final class Contoller {
private static final Logger logger = LoggerFactory.getLogger(LiabilityContoller.class);
#Autowired
DemoService demoService;
#GetMapping("/report")
public ResponseEntity<String> getCollection(#RequestParam(name="cohort")String cohort){
List<Book> collection=demoService.getRecords(amount);
bookUtils.writeDataIntoCSVFile(collection, amount);
uploadReportsFilesToSftp(amount);
return new ResponseEntity<String>(" Files Are Generated", HttpStatus.OK);
}
Service class:
#Service
public class DemoService{
#Autowired
DemoRepository demoRepository;
public List<Liability> getRecords(String amount) {
List<Book> list=demoRepository.getRecordsByAmount(amount);
return list;
}
}
Repository
public interface DemoRepository extends JpaRepository<Book,Long>{
#Query(value="Select name,amount from Book where amount=:amount",
List<Book>getRecordsByAmount(String amount);
}
Utility:
public static void writeDataIntoReportsCSVFile(final List<Book> collection,final String amount,final String sftpLocalFile) {
try {
FileWriter FileWriter1 = new FileWriter(sftpLocalFile+"demo.csv");
CSVPrinter FilePrinter1 = new CSVPrinter(FileWriter1, CSVFormat.EXCEL);
printReportsHeader(FileWriter1);
for (Liability obj : collection) {
FilePrinter1.printRecord(obj.getName(),obj.getAmount());
}
FilePrinter1.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
public static void printReportsHeader(CSVPrinter FilePrinter1) {
try {
FilePrinter1.printRecord("NAME","AMOUNT");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
How could I test this controller what could be the best way to do so?
When you want to unit test a Spring application, the high level idea would be to write tests for the repository layer first, then write unit tests for the service, and then finally the controller layer.
Assuming that you have unit tests for DemoService, bookUtils, and uploadReportsFilesToSftp the Test for the controller would be exactly as below.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.ArgumentMatchers.any;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#ExtendWith(SpringExtension.class)
#WebMvcTest(Controller.class)
class ControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private DemoService jsonFileService;
#Test
void getCollection() throws Exception {
Mockito.doNothing().when(jsonFileService.getRecords(any()));
// similar stubbings here
this.mockMvc.perform(
get("/report")
.param("cohort", "testCohortValue")
).andExpect(
status().isOk()
);
}
}

How to unit test a method which uses spring retry mechanism

I am trying to write a Junit method that uses the spring retry mechanism to re-invoke a failed operation.
But I am not able to verify spring retry working properly with JUnit.
public interface StudentService{
public void addStudent(Student student);
}
#Service
public class StudentServiceImpl {
#Autowired
SomeService someService;
#Transactional
// InternalServerErrorException runtime exception
#Retryable(value = {InternalServerErrorException.class},
maxAttempts=6)
public void addStudent(Student student){
try{
someService.addStudent(student);
}catch(Exception e){
throw new InternalServerErrorException("unable to add student");
}
}
}
#Configuration
##EnableRetry
public class AppConfig{
}
//
#RunWith(SpringJUnit4ClassRunner.class)
public class StudentServiceImplTest(){
#InjectMocks
StudentServiceImpl classUnderTest;
#Mock
SomeService someService;
public void testAddStudent(){
//ARRANGE
Student student= new Student("John","A123") // name, Id
doThrow(InternalServerErrorException).doNothing().when(someService).addStudent(student);
//ACT
classUnderTest.addStudent(student);
//ASSERT 1st attempt got exception , 2nd attempt success
// Always failed with exception
verify(someService, times(2)).addStudent(any());
}
}
// getting following exception
com.studentapp.exceptions.InternalServerErrorException: unable to add student
#InjectMocks
StudentServiceImpl classUnderTest;
You are injecting it as a Mock instead of using Spring #Autowired to get the full Spring proxy with the retry interceptor.

Set authentication for Integration test of secured Controller

I cannot set authentication for my integration test of rest controller. Controller's method looks like this:
#RestController
#RequestMapping(BASE_URL)
#RequiredArgsConstructor
public class EventController {
public static final String BASE_URL = "/api/event";
#PostMapping
#PreAuthorize("hasRole('USER')")
public void createEvent() {
System.out.println("I am in controller");
}
}
and here is my test:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class EventControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#BeforeEach
public void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
#Test
void create() throws Exception {
this.mockMvc.perform(post(EventController.BASE_URL)
.with(authentication(new UsernamePasswordAuthenticationToken(
new MyPrincipal(100, "denis"),
null,
Collections.singletonList(new SimpleGrantedAuthority("USER"))
)))
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType("application/json"));
}
My test always failed due to status 401 so my mocked authentication doesn't work. Can you tell me how to fix it? Thank you in advice.
The easiest way to test secured requests is to use #WithMockUser(A_ROLE)
so your test could look like
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
#SpringBootTest
#AutoConfigureMockMvc
class EventControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
#WithMockUser("USER")
void create() throws Exception {
this.mockMvc.perform(post(EventController.BASE_URL)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType("application/json"));
}
}
Some remarks:
your test expects a result, so adopt your controller or test
#PostMapping
#PreAuthorize("hasRole('ROLE_USER')")
public ResponseEntity<String> createEvent() {
String result = "I am in controller";
System.out.println(result);
return ResponseEntity.ok().body(result);
}
you are doing/testing a POST so make sure that in your security configuration you do http.csrf().disable()....
or provide a csrf-token in your test
this.mockMvc.perform(post(EventController.BASE_URL)
.with(SecurityMockMvcRequestPostProcessors.csrf()) // provide a csrf-token
....
'

#ControllerAdvice never gets triggered in Spring Boot

I am trying to create my own custom response for all types of RestClientResponseException in my Spring Boot Application
Custom exception thrown from Controller class :
throw new HealthCheckRestException(ex.getResponseBodyAsString());
My ExceptionHandler class goes like this :
#Order(Ordered.HIGHEST_PRECEDENCE)
#EnableWebMvc
#ControllerAdvice
public class AvailExceptionHandler {
private static final Logger logger = Logger.getLogger(AvailExceptionHandler.class);
#ExceptionHandler(value=HealthCheckRestException.class)
public AvailResponse handleHttpErrorResponse(final HealthCheckRestException ex, final HttpServletRequest request){
logger.error("RestClientException is thrown from HealthCheck API: with status :"
+ ex.getStatusText() + " and exception : "+ ex.getMessage());
return new AvailResponse(AvailStatus.ERROR);
}
}
I have tried all possible cases like :
1) Trying #ExceptionHandler inside the controller class
2) Including #ControllerAdvice(basePackages = "com.org.availabillity.utilities") to scan for specific packages where the controller is defined.
3) Using #Order(Ordered.HIGHEST_PRECEDENCE) to set precedence
4) Using #RestControllerAdvice
Nothing seems to intercept after the exception is thrown and call the method annotated with #ExceptionHandler
Stuck for sometime now on this and need some help.
Much appreciate your help on this.
I am using spring-web-5.0.6.RELEASE
Try with something like this:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import java.util.Map;
#RestControllerAdvice
#Order(Ordered.HIGHEST_PRECEDENCE)
public final class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
private static final boolean INCLUDE_STACKTRACE = false;
#ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, Object>> handle(final WebRequest request, final Throwable exception) {
log.debug("Fallback handler executed for unhandled exception:", exception);
return new ResponseEntity<>(new DefaultErrorAttributes().getErrorAttributes(request, INCLUDE_STACKTRACE),
HttpStatus.INTERNAL_SERVER_ERROR);
}
// IllegalArgumentException is not "entirely correct", so replace this with your own
#ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<Map<String, Object>> handle1(final WebRequest request, final Throwable exception) {
log.debug("Fallback handler executed for unhandled exception:", exception);
return new ResponseEntity<>(new DefaultErrorAttributes().getErrorAttributes(request, INCLUDE_STACKTRACE),
HttpStatus.INTERNAL_SERVER_ERROR);
}
// TODO: Keep adding exception(s) handlers for particular cases
}
GlobalExceptionHandler is just a Spring component/bean that can be placed anywhere, provided you configured that location to be discovered.
#EnableWebMvc should be placed in a class annotated with #Configuration. Moreover, if you are using Spring Boot, chances are you don't need it because it will be inferred.
Here is the small example from my Handler class;
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler({QueryNotFoundException.class,ContentNotAllowedException.class})
public final ResponseEntity<ApiError> handleException(Exception ex, WebRequest request) {
HttpHeaders headers = new HttpHeaders();
if (ex instanceof QueryNotFoundException) {
HttpStatus status = HttpStatus.NOT_FOUND;
QueryNotFoundException qnfe = (QueryNotFoundException) ex;
return handleQueryNotFoundException(qnfe, headers, status, request);
} else if (ex instanceof ContentNotAllowedException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
ContentNotAllowedException cnae = (ContentNotAllowedException) ex;
return handleContentNotAllowedException(cnae, headers, status, request);
} else {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return handleExceptionInternal(ex, null, headers, status, request);
}

Testing Spring MVC #ExceptionHandler method with Spring MVC Test

I have the following simple controller to catch any unexpected exceptions:
#ControllerAdvice
public class ExceptionController {
#ExceptionHandler(Throwable.class)
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
public ResponseEntity handleException(Throwable ex) {
return ResponseEntityFactory.internalServerErrorResponse("Unexpected error has occurred.", ex);
}
}
I'm trying to write an integration test using Spring MVC Test framework. This is what I have so far:
#RunWith(MockitoJUnitRunner.class)
public class ExceptionControllerTest {
private MockMvc mockMvc;
#Mock
private StatusController statusController;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(new ExceptionController(), statusController).build();
}
#Test
public void checkUnexpectedExceptionsAreCaughtAndStatusCode500IsReturnedInResponse() throws Exception {
when(statusController.checkHealth()).thenThrow(new RuntimeException("Unexpected Exception"));
mockMvc.perform(get("/api/status"))
.andDo(print())
.andExpect(status().isInternalServerError())
.andExpect(jsonPath("$.error").value("Unexpected Exception"));
}
}
I register the ExceptionController and a mock StatusController in the Spring MVC infrastructure.
In the test method I setup an expectation to throw an exception from the StatusController.
The exception is being thrown, but the ExceptionController isn't dealing with it.
I want to be able to test that the ExceptionController gets exceptions and returns an appropriate response.
Any thoughts on why this doesn't work and how I should do this kind of test?
Thanks.
I just had the same issue and the following works for me:
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(statusController)
.setControllerAdvice(new ExceptionController())
.build();
}
This code will add ability to use your exceptions controlled advice.
#Before
public void setup() {
this.mockMvc = standaloneSetup(commandsController)
.setHandlerExceptionResolvers(withExceptionControllerAdvice())
.setMessageConverters(new MappingJackson2HttpMessageConverter()).build();
}
private ExceptionHandlerExceptionResolver withExceptionControllerAdvice() {
final ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() {
#Override
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(final HandlerMethod handlerMethod,
final Exception exception) {
Method method = new ExceptionHandlerMethodResolver(ExceptionController.class).resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(new ExceptionController(), method);
}
return super.getExceptionHandlerMethod(handlerMethod, exception);
}
};
exceptionResolver.afterPropertiesSet();
return exceptionResolver;
}
Since you are using stand alone setup test you need to provide exception handler manually.
mockMvc= MockMvcBuilders.standaloneSetup(adminCategoryController).setSingleView(view)
.setHandlerExceptionResolvers(getSimpleMappingExceptionResolver()).build();
I had same problem a few days back, you can see my problem and solution answered by myself here Spring MVC Controller Exception Test
Hoping my answer help you out
Use Spring MockMVC to emulate a servletContainer to a point where you can incorporate any request filtering or exception handling tests in your unit tests suite.
You can configure this setup with the following approach:
Given a custom RecordNotFound exception...
#ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Record not found") //
public class RecordNotFoundException extends RuntimeException {
private static final long serialVersionUID = 8857378116992711720L;
public RecordNotFoundException() {
super();
}
public RecordNotFoundException(String message) {
super(message);
}
}
... and a RecordNotFoundExceptionHandler
#Slf4j
#ControllerAdvice
public class BusinessExceptionHandler {
#ExceptionHandler(value = RecordNotFoundException.class)
public ResponseEntity<String> handleRecordNotFoundException(
RecordNotFoundException e,
WebRequest request) {
//Logs
LogError logging = new LogError("RecordNotFoundException",
HttpStatus.NOT_FOUND,
request.getDescription(true));
log.info(logging.toJson());
//Http error message
HttpErrorResponse response = new HttpErrorResponse(logging.getStatus(), e.getMessage());
return new ResponseEntity<>(response.toJson(),
HeaderFactory.getErrorHeaders(),
response.getStatus());
}
...
}
Configure a tailored test context: set a #ContextConfiguration to specify the classes you need for your test. Set Mockito MockMvc as a servlet container emulator and set your tests fixture and dependencies.
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {
WebConfig.class,
HeaderFactory.class,
})
#Slf4j
public class OrganisationCtrlTest {
private MockMvc mvc;
private Organisation coorg;
#MockBean
private OrganisationSvc service;
#InjectMocks
private OrganisationCtrl controller = new OrganisationCtrl();
//Constructor
public OrganisationCtrlTest() {
}
....
Configure a mock MVC "servlet emulator": register handler beans in the context and build the mockMvc emulator (Note: there are two possible configuration: standaloneSetup or webAppContextSetup; refer to the documentation). The builder rightfully implements the Builder pattern so you can chain configuration commands for exception resolvers and handlers before calling build().
#Before
public void setUp() {
final StaticApplicationContext appContext = new StaticApplicationContext();
appContext.registerBeanDefinition("BusinessExceptionHandler",
new RootBeanDefinition(BusinessExceptionHandler.class, null, null));
//InternalExceptionHandler extends ResponseEntityExceptionHandler to //handle Spring internally throwned exception
appContext.registerBeanDefinition("InternalExceptionHandler",
new RootBeanDefinition(InternalExceptionHandler.class, null,
null));
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.standaloneSetup(controller)
.setHandlerExceptionResolvers(getExceptionResolver(appContext))
.build();
coorg = OrganisationFixture.getFixture("orgID", "name", "webSiteUrl");
}
....
Get the exception resolver
private ExceptionHandlerExceptionResolver getExceptionResolver(
StaticApplicationContext context) {
ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();
resolver.getMessageConverters().add(
new MappingJackson2HttpMessageConverter());
resolver.setApplicationContext(context);
resolver.afterPropertiesSet();
return resolver;
}
Run your tests
#Test
public void testGetSingleOrganisationRecordAnd404() throws Exception {
System.out.println("testGetSingleOrganisationRecordAndSuccess");
String request = "/orgs/{id}";
log.info("Request URL: " + request);
when(service.getOrganisation(anyString())).
thenReturn(coorg);
this.mvc.perform(get(request)
.accept("application/json")
.andExpect(content().contentType(
.APPLICATION_JSON))
.andExpect(status().notFound())
.andDo(print());
}
....
}
Hope this helps.
Jake.
Try it;
#RunWith(value = SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration(classes = { MVCConfig.class, CoreConfig.class,
PopulaterConfiguration.class })
public class ExceptionControllerTest {
private MockMvc mockMvc;
#Mock
private StatusController statusController;
#Autowired
private WebApplicationContext wac;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void checkUnexpectedExceptionsAreCaughtAndStatusCode500IsReturnedInResponse() throws Exception {
when(statusController.checkHealth()).thenThrow(new RuntimeException("Unexpected Exception"));
mockMvc.perform(get("/api/status"))
.andDo(print())
.andExpect(status().isInternalServerError())
.andExpect(jsonPath("$.error").value("Unexpected Exception"));
}
}
This is better:
((HandlerExceptionResolverComposite) wac.getBean("handlerExceptionResolver")).getExceptionResolvers().get(0)
And do not forget to scan for #ControllerAdvice beans in your #Configuration class:
#ComponentScan(basePackages = {"com.company.exception"})
...tested on Spring 4.0.2.RELEASE

Resources