Mockito and Deffered Result - java

I have a spring based project and I am trying to improve the code coverage within it
I have the following block of code which uses a lambda on the defferedResult onCompletion method
util.getResponse(userInfoDeferredResult, url, userName, password);
userInfoDeferredResult.onCompletion(() -> {
//the result can be a String or ErrorResponse
//if ErrorResponse we don't want to cause ClassCastException and we don't need to cache the result
if (userInfoDeferredResult.getResult() instanceof String){
String response = (String) userInfoDeferredResult.getResult();
cacheServices.addValueToCache(Service.USER_INFO_CACHE_NAME, corpId, response);
}
});
I was wondering - is it possible to mock the contents of the onCompletion lambda using mockito or powerMockito?

Extract contents to new method:
if(userInfoDeferredResult.getResult() instanceof String) {
String response = (String) userInfoDeferredResult.getResult();
cacheServices.addValueToCache(Service.USER_INFO_CACHE_NAME, corpId, response);
}
Then test the method that way?

Your test should rather mock cacheServices, and execute the lambda.

Extracting contents to new method is good solution in this case as mentioned in other answer.
Also, you can find article about in the following link:
http://radar.oreilly.com/2014/12/unit-testing-java-8-lambda-expressions-and-streams.html

Generally I prefer not to change the service code for test code (for example extracting to method and make it public although it should be private). The onCompletion method is triggered when the completed of AsyncContext is called. So you can test in the following way:
#RunWith(SpringRunner.class)
#WebMvcTest(DeferredResultController.class)
public class DeferredResultControllerUnitTest {
#MockBean
CacheServices cacheServices;
#Autowired
private MockMvc mockMvc;
#Test
public void onCompletionTest() throws Exception {
mockMvc.perform(get("/your_url"))
.andDo(mvcResult -> mvcResult.getRequest().getAsyncContext().complete());
Mockito.verify(cacheServices)
.addValueToCache(Service.USER_INFO_CACHE_NAME, getExpextedCorpId(), getExpectedResponse());
}
}
Working github example here.
In contrast to #SpringBootTest, #WebMvcTest won't start the all Spring application context so it is more lighter.

Related

How to test with Postman a controller's method that has one or more objects parameters (and not simple params) in Spring?

I am a newbie in Spring development. I need to create a simple application, a controller that has a method that takes as parameter an object of a custom designed entity class into the project. The prototype looks like this:
#RestController
public class JobsController {
#PostMapping("/search")
public ResponseEntity<?> search() {
log.info("JobsController -> search method");
//JobSearchEntity jobSearchEntity = modelMapper.map(jobSearch, JobSearchEntity.class);
List<JobEntity> jobs = jobService.searchJobs();
//log.info(String.format("Job found: %s ", jobSearch));
return ResponseEntity.ok(jobs);
}
}
Can someone who is more advanced into this staff with Postman testing tell me how to do that , how to test a controller method which takes parameters?
You can use postman to submit parameters in JSON format after adding # requestbody annotation on the method, or submit parameters directly in form without annotation
You can use this example. Is very simple exemple.
#RestController
#RequestMapping("/root")
public class RootController {
private final RootService service;
public RootController(final RootService service) {
this.service = service;
}
#PostMapping("/exemple")
public void createRoot(#RequestBody final RootDto dto) {
service.createRoot(dto);
}
}
Then you can send request to POST host/root/exemple with your JSON.
More exampls you can find here: https://www.baeldung.com/spring-request-response-body
It seems you are missing an honest search on google about the subject.
You can make use of #RequestBody annotation to accept method arguments.
Check these page for examples --
#RequestBody and #ResponseBody annotations in Spring
https://stackabuse.com/get-http-post-body-in-spring/
https://www.twilio.com/blog/create-rest-apis-java-spring-boot
These set of playlist on youtube are very good starter course for SpringBoot -
https://www.youtube.com/c/JavaBrainsChannel/playlists
Postman Tutorial--
https://www.youtube.com/watch?v=VywxIQ2ZXw4
To get data from api is preferred to use GET method :
#RestController
public class JobsController {
#GetMapping("/search")
public ResponseEntity<?> search(#RequestParam("id") String id,#RequestParam("desc") String desc) {
log.info("JobsController -> search method");
//JobSearchEntity jobSearchEntity = modelMapper.map(jobSearch, JobSearchEntity.class);
List<JobEntity> jobs = jobService.searchJobs();
//log.info(String.format("Job found: %s ", jobSearch));
return ResponseEntity.ok(jobs);
}
}
you call this api with post man this way :
#PostMapping used usually to save new data (example : create job )
Take look on rest resource naming guide

How to wrap an annotation and conditionally applies it to a method

Say I have an annotation (#RequiresAccount) introduced in another library and I'm using it in my project, is there a way to conditionally apply it to a method, e.g. apply it when the customer is from website A and not apply when customer is from website B?
I've taken a look and the only possibility I've found was, creating a wrapper-Annotation:
#Aspect
#Component
public class RequiresAccountWrapperAspect {
#Autowired
private HttpServletRequest request;
private RequiresAccountAspect requiresAccountAspect = new RequiresAccountAspect();
#Around("#annotation(com.example.demo.components.RequiresAccountWrapper)")
public Object checkIfRequiresAccount(ProceedingJoinPoint joinPoint) throws Throwable {
String requestURL = request.getRequestURL().toString();
if (requestURL.startsWith("http://localhost")) {
requiresAccountAspect.checkAccount(joinPoint);
}
return joinPoint.proceed();
}
}
So everywhere you've used your RequiresAccount annotation, you can use this wrapper instead. For example:
#GetMapping("/test")
#RequiresAccountWrapper
public String h() {
return "test";
}
As you can see I'm creating a new instance of the aspect. I don't know if you have access to the Aspect-class itself but if you have you can then call the method in it and pass the joinPoint. To find the URL from the request you can inject the HttpServletRequest.

Spring's WebClient hanging on bodyToMono(Pojo) with MockServer

I want to mock an external API, which I am calling as part of my service. Therefore, I wanted to use the MockWebServer from okhttp3. My problem is that the call to bodyToMono works fine, if I want to retrieve the body as string, but does not work when retrieving it as data class. I tried to trim it down using the following code snippet:
public class MockWebClientTest {
private MockWebServer server;
#BeforeEach
public void setUp() throws IOException {
server = new MockWebServer();
server.start(9876);
}
#AfterEach
public void tearDown() throws IOException {
server.shutdown();
}
#Test
public void stringWorks() throws JsonProcessingException {
createMockedTokenCall();
Mono<String> response = WebClient.create(this.server.url("/").toString())
.get()
.uri("/")
.retrieve()
.bodyToMono(String.class);
System.out.println(response.block());
}
#Test
public void classDoesNotWork() {
createMockedTokenCall();
Mono<AuthToken> response = WebClient.create(this.server.url("/").toString())
.get()
.uri("/")
.retrieve()
.bodyToMono(AuthToken.class);
System.out.println(response.block());
}
private void createMockedTokenCall() {
server.enqueue(new MockResponse().setBody("{\"accessToken\":\"BARBAR\",\"refreshToken\":\"FOOFOO\"}"));
}
}
class AuthToken {
private String accessToken;
private String refreshToken;
//constructor
}
The first Test (stringWorks) is working fine and return the correct json representation. However, the second test (classDoesNotWork) hangs forever on the bodyToMono call.
My guess is that it has nothing to do with the okttp3 library directly, since I had the same error using Wiremock. The same code works however when targeting a real API endpoint. Unfortunately, I could not find another way to test my calls using WebClient since Spring currently has no direct mocking support for it (see SPR-15286).
I am really looking forward to help on that matter! Thanks in advance!
General remark: This is basically a more or less copy of test case shouldReceiveJsonAsPojo in https://github.com/spring-projects/spring-framework/blob/master/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientIntegrationTests.java
Ok, after looking at the linked test and doing a bit of comparison, I found the solution (or my bug so to say): I forgot the correct Content-Type header. So it works using the following:
private void createMockedTokenCall() {
server.enqueue(new MockResponse().setHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE).setBody("{\"accessToken\":\"BARBAR\",\"refreshToken\":\"FOOFOO\"}"));
}
Apart from me doing a mistake, I really think that this should not result in an infinite hanging of the application...

Clean up redirect URL in Mockito tests

I have some Mockito tests which use MockMvc for controllers testing.
Controllers are using #ControllerAdvice which includes #ModelAttribute. It is needed because there are few attributes needed by 90% of controllers.
I clean up this information from redirect URLs by ignoreDefaultModelOnRedirect (it seems more efficient than adding RedirectAttributes parameters in all methods).
I need to test redirectUrl. But in this URL I see url + redirect attributes. I don't want to check with them because it is not how production behavior looks like.
Is there any way to pass ignoreDefaultModelOnRedirect to MockMvc?
== Edited
User go to greeting -> Redirected to greetingRedirected
#Controller
public class GreetingController {
#GetMapping("/greeting")
public String greeting(Model model) {
return "redirect:/greetingRedirected";
}
}
Model enriched with #ControllerAdvice data
#ControllerAdvice
public class GreetingControllerAdvice {
#ModelAttribute("attr")
public String getAttr() {
return "ATTRIBUTE";
}
}
And now start simple test:
#RunWith(MockitoJUnitRunner.class)
public class ApplicationTest {
private MockMvc mockMvc;
#Before
public void setUp() {
GreetingController greetingController = new GreetingController();
mockMvc = MockMvcBuilders.standaloneSetup(greetingController).setControllerAdvice(new GreetingControllerAdvice()).build();
}
#Test
public void greeting() throws Exception {
mockMvc.perform(get("/greeting"))
.andExpect(redirectedUrl("/greetingRedirected"));
}
}
As the consequence the result is:
java.lang.AssertionError: Redirected URL
Expected :/greetingRedirected
Actual :/greetingRedirected?attr=ATTRIBUTE
I can use SpringRunner of course and #Autowire MockMvc but MockitoRunner is faster (which is important for big project with thousands of tests).
I can get rid of setControllerAdvice(...) but it will make behavior much less similar to production behavior for most of the cases(because I have lots of tests which do not check on redirection behavior).
Finally I can split tests on "redirected tests" (with disabled advice) and "not redirected tests" but it will be quite silly. :)
So I'm asking if there is any simple way to tell MockMvc/Mockito that parameter spring.mvc.ignore-default-model-on-redirect is enabled and should behave like expected.

spring mvc how to test that my service persists entities in post request

So I'm writing this web app with Spring Boot using Spring Data with JPA and Spring MVC and I would like to make mock controller tests. I figured out how to test the get method, but in my controllers post method a new JPA entity is being either persisted or updated with my service. Here is what my controller looks like:
#Controller
#RequestMapping("/registerMember")
public class RegisterMemberController {
#Autowired
private MemberService memberService;
#GetMapping
public String index(RegisterMemberBean registerMemberBean) {
return "registerMember";
}
#PostMapping
public String handleSubmit(#Valid RegisterMemberBean registerMemberBean, BindingResult bindingResult, Model model) {
Member member = registerMemberBean.getMember();
boolean isRepeatPasswordCorrect = !isRepeatPasswordIncorrect(member.getPassword(), registerMemberBean.getComparePassword());
if(isAnyErrors(isRepeatPasswordCorrect, !bindingResult.hasErrors())) {
if(!isRepeatPasswordCorrect) {
model.addAttribute("isRepeatPasswordIncorrect", true).
addAttribute("isRepeatPasswordIncorrectMsg", "Passwords don't match");
}
return "registerMember";
}
boolean errUsername = !memberService.isNoOtherEntityWithUserName(0, member.getUserName());
boolean errEmail = !memberService.isNoOtherEntityWithEmail(0, member.getEmail());
if(errUsername || errEmail) {
if(errUsername) {
model.addAttribute("isExistingUserName", true).addAttribute("isExistingUserNameMsg", "Already a user with that username");
} if(errEmail) {
model.addAttribute("isExistingEmail", true).addAttribute("isExistingEmailMsg", "Already a user with that email");
}
return "registerMember";
}
getMainService().save(member);
return redirectTo("index", new RedirectEntity("member", member.getId()));
}
}
Now in my mock controller test i want to make make sure that my post method does the following:
Reload the page if the BindingResults has any errors
My service persists the member JPA entity in db (if no errors)
Method redirects me to the index page
This is what my (poor) test class looks like so far:
#RunWith(SpringRunner.class)
#TestPropertySource(locations="classpath:application_test.properties")
#WebAppConfiguration
public class RegisterMemberControllerTest {
private MockMvc mockMvc;
#MockBean
private MemberService memberService;
#MockBean
private RegisterMemberController controller;
#Before
public void init() {
mockMvc = MockMvcBuilders.standaloneSetup(controller).setViewResolvers(new StandaloneMvcTestViewResolver()).build();
controller.setMainService(memberService);
}
#Test
public void testIndex() throws Exception {
mockMvc.perform(get("/registerMember"))
.andExpect(status().isOk())
.andExpect(forwardedUrl("registerMember");
}
#Test
public void testHandleSubmit() throws Exception {
RegisterMemberBean registerMemberBean = new RegisterMemberBean();
registerMemberBean.setMember(TestFixture.getValidMemberWithoutReferences());
Member member = TestFixture.getValidMember();
mockMvc.perform(post(Page.REGISTER_MEMBER)).andExpect(status().isOk());
when(mockMvc.perform(post(Page.REGISTER_MEMBER)).andExpect((ResultMatcher) memberService.save(member)).andExpect(forwardedUrl("redirect:/index/member=" + member.getId() + ".html")));
}
}
to my understanding spring boot uses Mockito. I have some experience with EasyMock but I would like to use the spring defaults as much as possible. Can someone show how to achieve this?
I think there is a little bit of confusion on what should and shouldn't be mocked.
If I read your question correctly, you are actually trying to Unit Test your RegisterMemberController. Therefore, you most likely should NOT make a mock of that class, but actually test that class.
I believe that you would be creating fakes/dummies/stubs/mocks/spies of your MemberService, RegisterMemberBean, and BindingResult classes.
It would be these classes that would be created by your unit test and handed to your controller during the test that will force the testing of the logic that you are interested in proving/disproving.
FYI, when verifying that the MemberService class was called, that is where you would use a mock. The rest of the classes could either be dummies or stubs.
Side Note: I would recommend removing the Model parameter from your handleSubmit() method since it doesn't seem to be used anywhere.

Categories