How can I refactor my spring boot controller code to mock new instance creation in Mockito? - spring-mvc

I have the following controller. I want to write unit test for this using JUnit and Mockito. The problem I'm facing is that mockito doesn't allow mocking of new instance creation that I'm doing with RestUserContext at the beginning of the method. How should I refactor my controller code?
#Autowired
private HttpServletRequest httpServletRequest;
#GetMapping(value="my/application/path", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity getStatistics(#PathVariable(name="userId") String userId) throws Exception {
RestUserContext userContext = new RestUserContext(httpServletRequest);
boolean authorized = isAuthorized(brandId, userContext);
if (!authorized) {
log.error("Log error");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new ErrorResponseDto(HttpStatus.UNAUTHORIZED.value(),HttpStatus.UNAUTHORIZED.getReasonPhrase()));
}
ApplicationDto appDto = getMessageStatisticsData(userId, userContext);
return ResponseEntity.status(HttpStatus.OK).body(appDto);
}

Related

Mockito test for service call on Legacy code

I am creating junit test case for legacy spring controller code which calls service (creates new instance of service call instead of autowire/spring bean). I want the service class call to be mocked. Please let know if it's possible. Changing the legacy code is not an option for me.
public class WebController {
#PostMapping("/api/op1")
public #ResponseBody String validate(ModelMap model,HttpSession session,HttpServletRequest request){
---
--
Service service = new Service();
service.invoke(param1,param2);
----
---
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
public class WebControllerTest{
private MockMvc mockMvc;
private WebController classUnderTest;
#Test
public void validat() throws Exception {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
//Service service = spy(Service.class);
//doReturn(new ServiceCallResponse()).when(service).validate( any(String.class),any(String.class));
Service service = Mockito.mock( Service.class, CALLS_REAL_METHODS );
doReturn(new ServiceCallResponse()).when(service).validate(any(String.class),any(String.class));
MvcResult result = mockMvc.perform(
post("/validate").params(params).session(new MockHttpSession()))
.andExpect(status().isOk())
.andReturn();
}
}
Spy and Mocktio calls real method options are not working. Please let know how to mock it.

No mapping found for HTTP request with URI [/api/transactions] in DispatcherServlet with name ''

I thought this is a standard configuration. But I get a 404 back. Where else should I configure Spring Boot ?
#RestController
#RequestMapping("/api")
public class TransactionStatisticsController {
public static final Logger logger = LoggerFactory.getLogger(TransactionStatisticsController.class);
#RequestMapping(value = "/transactions",
method = RequestMethod.POST)
public ResponseEntity sendTransaction(#RequestBody Transaction request) {
logger.info( request.toString());
return new ResponseEntity(HttpStatus.OK);
}
}
This is my test.
#JsonTest
#SpringBootTest(classes = Application.class)
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
public class TransactionStatisticsRestTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private JacksonTester<Transaction> json;
private static Transaction transaction;
#BeforeClass
public static void createTransaction(){
BigDecimal amount = new BigDecimal(12.3343);
transaction = new Transaction(amount.toString(),
"2010-10-02T12:23:23Z");
}
#Test
public void getTransactionStatus() throws Exception {
final String transactionJson = json.write(transaction).getJson();
mockMvc
.perform(post("/api/transactions")
.content(transactionJson)
.contentType(APPLICATION_JSON_UTF8))
.andExpect(status().isOk());
}
public static byte[] convertObjectToJsonBytes(Object object) throws IOException {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsBytes(transaction);
}
}
Request being made is
MockHttpServletRequest:
HTTP Method = POST
Request URI = /api/transactions
Parameters = {}
Headers = {Content-Type=[application/json;charset=UTF-8]}
Body = {"amount":"12.3343000000000007077005648170597851276397705078125","timestamp":"2010-10-02T12:23:23Z[UTC]"}
Session Attrs = {}
Handler:
Type = null
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 404
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Update : I added a component scan pointing to a base package. I don't see that error now. Please see the comments where there is an answer.
As in the comment section ,there was only requirement was to bind a component scan base package location .
#Component scan -->Configures component scanning directives for use with #Configuration classes. Provides support parallel with Spring XML's element.
Either basePackageClasses() or basePackages() (or its alias value()) may be specified to define specific packages to scan. If specific packages are not defined, scanning will occur from the package of the class that declares this annotation.
Please share your project folder architecture. It might be possible that your controller package is out of the main class package. That's why it is showing 404.
This code :
#RestController
#RequestMapping("/api")
public class TransactionStatisticsController {
public static final Logger logger = LoggerFactory.getLogger(TransactionStatisticsController.class);
#RequestMapping(value = "/transactions",
method = RequestMethod.POST)
public ResponseEntity sendTransaction(#RequestBody Transaction request) {
logger.info( request.toString());
return new ResponseEntity(HttpStatus.OK);
}
}
This should be into your main package where
#SpringBootApplication
public class YourApplication {
public static void main(String[] args) {
SpringApplication.run(YourApplication.class, args);
}
}
this main class resides.
I hope, this will help.
Seems using #JsonTest does not even allow to load Application Context, results mapping is not loaded and its throw 404 so #JsonTest is not a replacement for #SpringBootTest, it is a way to easily test json serialization/de-serialization.
As per documentation:
you can use the #JsonTest annotation. #JsonTest auto-configures the
available supported JSON mapper, which can be one of the following
libraries:
Jackson ObjectMapper, any #JsonComponent beans and any Jackson Modules
Gson
Jsonb
If by using Gson and removing #JsonTest your test run fine..(add Gson Dependency in pom)
#SpringBootTest
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
public class DemoKj01ApplicationTests {
#Autowired
private MockMvc mockMvc;
private static Transaction transaction;
#BeforeClass
public static void createTransaction(){
BigDecimal amount = new BigDecimal(12.3343);
transaction = new Transaction(amount.toString(),
"2010-10-02T12:23:23Z");
}
#Test
public void getTransactionStatus() throws Exception {
//final String transactionJson = json.write(transaction).getJson();
Gson gson = new Gson();
String jsonRequest = gson.toJson(transaction);
mockMvc
.perform(post("/api/transactions")
.content(jsonRequest)
.contentType(APPLICATION_JSON_UTF8))
.andExpect(status().isOk());
}
It is beacause of the trailing slas in #RequestMapping(value = "/transactions/", method = RequestMethod.POST)
Remove it and it will be ok : value = "/transactions/" => value = "/transactions"

Spring MVC - PropertyEditor not called during ModelAttribute type conversion

Using Spring 3.2.3, I'm trying to implement a simple CRUD controller that handles REST-ful URLs. It relies on a PropertyEditor to convert a path variable to a BusinessService entity by loading it from an application service. Code is as follows:
#Controller
public class BusinessServiceController {
#Autowired
private BusinessServiceService businessSvcService;
public BusinessServiceController() {
}
#InitBinder
public void initBinder(final WebDataBinder binder) {
binder.registerCustomEditor(BusinessService.class, new BusinessServicePropertyEditor(businessSvcService));
}
#RequestMapping(value = "/ui/account/business-services/{businessSvc}", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ModelAndView update(#ModelAttribute("businessSvc") #Valid final BusinessService businessSvc, final BindingResult result,
final RedirectAttributes redirectAttribs) throws UnknownBusinessServiceException {
ModelAndView mav;
if (result.hasErrors()) {
mav = new ModelAndView("/business-service/edit");
}
else {
businessSvcService.updateBusinessService(XSecurity.principal().getId(), businessSvc);
mav = new ModelAndView("redirect:/ui/account/business-services");
redirectAttribs.addFlashAttribute("message", Message.info("businessService.updated", businessSvc.getTitle()));
}
return mav;
}
}
public class BusinessServicePropertyEditor extends PropertyEditorSupport {
private final BusinessServiceService businessSvcService;
public BusinessServicePropertyEditor(final BusinessServiceService businessSvcService) {
this.businessSvcService = businessSvcService;
}
#Override
public String getAsText() {
final BusinessService svc = (BusinessService) getValue();
return Long.toString(svc.getId());
}
#Override
public void setAsText(final String text) {
final BusinessService svc = businessSvcService.getBusinessService(Long.parseLong(text));
setValue(svc);
}
}
According to SPR-7608, starting from Spring 3.2, #ModelAttribute method argument resolution checks if a path variable by the same name exists (it does here), in which case it tries to convert that path variable's value to the target parameter type through registered Converters and PropertyEditors. This is not what I'm experiencing. When I inspect what ServletModelAttributeMethodProcessor does, it clearly uses the request DataBinder's ConversionService to perform type conversion, which does not consider registered PropertyEditors, and hence BusinessServicePropertyEditor#setAsText is never called.
Is this a configuration problem or an actual bug?
Thanks for your help!
Spring's ConversionService and Converters are replacement for standard Java Beans PropertyEditors.
You need to implement Converter instead of PropertyEditor if this feature is based purely on conversion service.
To register your custom converters in WebDataBinder you might use ConfigurableWebBindingInitializer or #InitBinder method.

How to setup OAuth2RestTemplate (Post Updated)

I am not sure if I have OAuth2RestTemplate configured correctly. I am getting the following error when I run the tester class.
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory#1df3248: defining beans [propertyConfigurer,dataSource,transactionManager,org.springframework.aop.config.internalAutoProxyCreator,org.springframework.transaction.annotation.AnnotationTransactionAttributeSource#0,org.springframework.transaction.interceptor.TransactionInterceptor#0,org.springframework.transaction.config.internalTransactionAdvisor,emf,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalPersistenceAnnotationProcessor,accountRepository,questionRepository,org.springframework.data.repository.core.support.RepositoryInterfaceAwareBeanPostProcessor#0,org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor#0,org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor#0,jpaQuestionService,jpaAccountService,passwordEncoder,accountHelper,tradeConfig,org.springframework.data.repository.core.support.RepositoryInterfaceAwareBeanPostProcessor#1,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor,baseOAuth2ProtectedResourceDetails,oAuth2ProtectedResourceDetails,accessTokenRequest,oAuth2ClientContext,oAuth2RestTemplate]; root of factory hierarchy
Exception in thread "main" error="access_denied", error_description="Unable to obtain a new access token for resource 'null'. The provider manager is not configured to support it."
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainNewAccessTokenInternal(AccessTokenProviderChain.java:146)
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:118)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:216)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:168)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:89)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:442)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:123)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:409)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:385)
at com..main(Tester.java:44)
Classes
#Configuration
public class AppConfig {
#Bean
//#Scope(value="singleton", proxyMode=ScopedProxyMode.INTERFACES)
public BaseOAuth2ProtectedResourceDetails baseOAuth2ProtectedResourceDetails(){
BaseOAuth2ProtectedResourceDetails baseOAuth2ProtectedResourceDetails = new BaseOAuth2ProtectedResourceDetails();
baseOAuth2ProtectedResourceDetails.setClientId(clientId);
baseOAuth2ProtectedResourceDetails.setClientSecret(clientSecret);
return baseOAuth2ProtectedResourceDetails;
}
#Bean
public DefaultAccessTokenRequest accessTokenRequest(){
return new DefaultAccessTokenRequest();
}
#Bean
public OAuth2ClientContext oAuth2ClientContext(){
return new DefaultOAuth2ClientContext(accessTokenRequest());
}
#Bean
public OAuth2RestTemplate oAuth2RestTemplate(){
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(baseOAuth2ProtectedResourceDetails(),oAuth2ClientContext());
return restTemplate;
}
}
Tester Class
public class Tester {
public static void main(String[] args) {
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath*:jpa-app-context.xml");
ctx.refresh();
EntityManagerFactory emf = (EntityManagerFactory) ctx.getBean("emf");
EntityManager em = emf.createEntityManager();
TransactionSynchronizationManager.bindResource(emf , new EntityManagerHolder(em));
OAuth2RestTemplate oAuth2RestTemplate = (OAuth2RestTemplate) ctx.getBean("oAuth2RestTemplate");
//OAuth2RestTemplate oAuth2RestTemplate = ctx.getBean(OAuth2RestTemplate.class);
String uri="https:api..";
Object obj = oAuth2RestTemplate.exchange(uri, HttpMethod.POST, null, Object.class);
System.out.println("Tester Object: "+ obj.toString());
}
}
I faced the same exception, but with another protected resource type.
Generally, the exception raises only when AccessTokenProviderChain can't find an appropriate *AccessTokenProvider for particular *ProtectedResourceDetails instance. Meaning, when you try to do the following:
ClientCredentialsResourceDetails resource = new ClientCredentialsResourceDetails();
resource.setAccessTokenUri(url);
resource.setClientId(clientId);
resource.setClientSecret(secret);
resource.setGrantType("password");
return resource;
The code expects a client_credentials grant type since we use a ClientCredentialsResourceDetails, but we pass password value.
Here the code that worked in my case:
private OAuth2ProtectedResourceDetails withOAuth2Authentication(final String url, final String clientId, final String secret) {
ClientCredentialsResourceDetails resource = new ClientCredentialsResourceDetails();
resource.setAccessTokenUri(url);
resource.setClientId(clientId);
resource.setClientSecret(secret);
// here you can provide additional properties such as scope etc.
return resource;
}
#Bean
RestTemplate callbackClientV2() {
AccessTokenRequest atr = new DefaultAccessTokenRequest();
return new OAuth2RestTemplate(
withOAuth2Authentication(v2ServerUrl, v2Username, v2Password),
new DefaultOAuth2ClientContext(atr)
);
}

Spring MVC test case

Am new to Spring MVC, i have written web servise using spring MVC and resteasy. My controller is working fine, now need to write testcase but i tried writtig but i never succed am also getting problem in autowiring.
#Controller
#Path("/searchapi")
public class SearchAPIController implements ISearchAPIController {
#Autowired
private ISearchAPIService srchapiservice;
#GET
#Path("/{domain}/{group}/search")
#Produces({"application/xml", "application/json"})
public Collections getSolrData(
#PathParam("domain") final String domain,
#PathParam("group") final String group,
#Context final UriInfo uriinfo) throws Exception {
System.out.println("LANDED IN get****************");
return srchapiservice.getData(domain, group, uriinfo);
}
}
can anyone give me sample code for Test case in spring mvc.
"Spring-MVC" Test case could seem like this using mock objects, for example we want to test my MyControllerToBeTest:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/spring.xml")
public class MyControllerTest {
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private MyControllerToBeTested controller;
private AnnotationMethodHandlerAdapter adapter;
#Autowired
private ApplicationContext applicationContext;
#Before
public void setUp() {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
response.setOutputStreamAccessAllowed(true);
controller = new MyControllerToBeTested();
adapter = new AnnotationMethodHandlerAdapter();
}
#Test
public void findRelatedVideosTest() throws Exception {
request.setRequestURI("/mypath");
request.setMethod("GET");
request.addParameter("myParam", "myValue");
adapter.handle(request, response, controller);
System.out.println(response.getContentAsString());
}
}
but i don't have any experience with REST resource testing, in your case RestEasy.
If you want to test the full service inside the container you can have a look at the REST Assured framework for Java. It makes it very easy to test and validate HTTP/REST-based services.

Resources