How to mock a builder pattern expression using mockito - elasticsearch

I have following piece of Elasticsearch code
SearchRequestBuilder builder = client.prepareSearch(indexRange).setTypes(module).setSize(json.getPageSize())
.setFrom(json.getOffset()).setFetchSource(prepareFieldListToLoad(), prepareFieldListToIgnore())
.setExplain(false);
How can i mock it for junit testing using mockito?

It isn't clear from this snippet how client gets instantiated. However that happens, you need to mock that client object. Once you have that mocked, you can set the expectations on it so that when its prepareSearch method is invoked, it returns something--likely another mock. And then you will need to set the expectations on that other mock returned by the prepareSearch call, and so on.
You'll end up needing many mocks due to the chaining:
a mock for client
a mock for the object returned by the prepareSearch call
a mock for the object returned by the setTypes call
a mock for the object returned by the setSize call
a mock for the object returned by the setFrom call
a mock for the object returned by the setFetchSource call
a mock for the object returned by the setExplain call
You could consider using a stub for the object returned by the prepareSearch call if those "set" methods are just simple setters. Then the test will call the real setters of the object, but it still allows you to mock out some other call on the object that is more extensive.
Or if that sounds like too much manual setup, you can use Mockito's RETURN_DEEP_STUBS:
https://www.javadoc.io/doc/org.mockito/mockito-core/2.6.9/org/mockito/Answers.html
Example of deep stubs from: https://idodevjobs.wordpress.com/2015/04/09/mockito-deep-stubs-example/
public class MockingMethodChains {
#Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Foo foo;
#Test
public void easyWayToMockMethodChaining(){
when(foo.getBar().getName()).thenReturn("Foo Bar");
assertEquals("Foo Bar", foo.getBar().getName());
}
}
Other sources:
http://jakegoulding.com/blog/2012/01/09/stubbing-builder-pattern-in-mockito/
How to mock a builder with mockito

Related

How to mock an #Async call in a Repository

I have created an example using Spring 6(6.0.0-M2 at the moment)/Spring Data JPA, and create a Repository like this.
public interface PostRepository extends JpaRepository<Post, UUID> {
#Async
CompletableFuture<List<Post>> readAllBy();
}
In the controller testing codes, I try to mock it as general mocking method, failed.
when(this.posts.readAllBy())
.thenReturn(CompletableFuture.completedFuture(
List.of(
Post.builder().title("test").content("content of test1").build(),
Post.builder().title("test2").content("content of test2").build()
)
)
);
When running the tests, I got the following mocking errors:
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.
The complete testing codes is here.
Update: If I remove the async config from the test context(that means async is disabled.), it works well. But how to do when async is enabled?

MockMvc access the MockHttpServletRequest object that mockMvc will use

Is there anyway to get a hold of the actual request object that mockMvc will use when you execute:
mockMvc.perform(RequestBuilder requestBuilder)
I know that I can build the request myself (i.e)
Integer id = new Integer(1);
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post("/myPath").param(Constants.ACTION, Constants.GET_DETAIL)
.param(Constants.ID, id.toString());
MockHttpServletRequest request = requestBuilder.buildRequest(wac.getServletContext());
but I cannot pass this request along as the mockMvc.perform method specifically only accepts the builder which will create a new instance of MockHttpServletRequest. I am using EasyMock which uses equals() in it's matchers (at least by default) and due to the lack of an equals() implementation in MockHttpServletRequest it simply compares the object ids. i.e
EasyMock.reset(localeHelper);
localeHelper.getLocale(request);
EasyMock.expectLastCall().andReturn(locale);
/* this matcher will always fail because the request object is rebuilt by the mockMvc.perform(requestBuilder) call
and MockHttpServletRequest does not have an equals() method that these mocking tools can fall back on for object equivalency */
EasyMock.replay(localeHelper);
You probably want a capture.
Capture<HttpServletRequest> capture = EasyMock.newCapture();
EasyMock.expect(localeHelper.getLocale(EasyMock.capture(capture))).andReturn(locale);
EasyMock.replay(localeHelper);
mockMvc.perform(requestBuilder);
HttpServletRequest request = capture.getValue();
// Then assert whatever you want on the `request` that was received in parameter by the mock

Unit testing with MockitoJunitRunner: thenReturn always returns null for any(...) matchers

Using Spring Boot 2.1.6.RELEASE. In an unit test with MockitoJunitRunner, I'm mocking a REST controller as follows:
#Mock
private MyController myController;
Then, I'm defining the expectations of the endpoint call as follows:
when (myController.myEndpoint(any(MyInputDto.class), any(OAuth2Authentication.class))).thenReturn(new ResponseEntity<MyOutputDto>(myOutputDto, HttpStatus.OK));
But the following call:
ResponseEntity<MyOutputDto> resp = myController.myEndpoint(any(MyInputDto.class), any(OAuth2Authentication.class));
assertNotNull(resp);
raises java.lang.AssertionError as resp is null. Why is that ? Of course, the two parameters are different instances between the when(...) clause and the call (probably both null), but my understanding is that they don't have to match as I'm using any(...) matcher, meaning that whatever these instances are, the return should be the same.
What am I missing here ?
You defined your mocked object to return a new ResponseEntity when its myEndpoint method is called with two parameters: any object of type MyInputDto and any object of type OAuth2Authentication.
But when you actually call the method using
ResponseEntity<MyOutputDto> resp = myController.myEndpoint(any(MyInputDto.class), any(OAuth2Authentication.class));
you don't call it with objects of those types, but with any matchers. That case wasn't mocked, and it certainly wasn't your intention to mock matcher arguments.
Calling with actual objects instead, like
ResponseEntity<MyOutputDto> resp = myController.myEndpoint(new MyInputDto(), new OAuth2Authentication());
would work, but that would not provide what you want to achieve by running a mocked test: it would just check if the mock returns what you defined, but it would't test your controller behaviour.
What you should do instead:
Don't mock the class you want to test; mock its dependencies.
private MyController myController;
#Mock
private MyInputDto inputDto;
#Mock
private OAuth2Authentication authentication;
// mock all method calls that are needed on dependencies
when(inputDto.someMethod(...)).thenReturn(...);
[...]
when(authentication.someMethod(...)).thenReturn(...);
[...]
Now you can test the actual behaviour of your test object:
ResponseEntity<MyOutputDto> resp = myController.myEnpoint(inputDto, authentication);
assertNotNull(resp);
A nice Mockito tutorial: https://www.vogella.com/tutorials/Mockito/article.html
Mockito even hints at something being wrong when trying to implement the test like you did in your question. Running your test I get
[MockitoHint] MockitoTest.test (see javadoc for MockitoHint):
[MockitoHint] 1. Unused... -> at MockitoTest.test(MockitoTest.java:24)
[MockitoHint] ...args ok? -> at MockitoTest.test(MockitoTest.java:26)
Line 24 refers to the line where the mock was stubbed (the when(...).thenReturn() part) because it is never used; line 26 referes to the calling line because it uses Matchers instead of objects.
You should see a similar output on your console when running the test.

Mockito: Spying function calls inside Functional interfaces?

Mockito doesn't seem to be able to spy on function calls inside Functional interfaces. Suppose I have a simple Spring Boot App with a service:
#Service
public class TestService {
Function<Integer, Integer> mapping = this::add2;
Integer add2(Integer integer) {
return integer + 2;
}
}
And a test:
#SpyBean
TestService testService;
#Test
public void mockitoTest2(){
doReturn(6).when(testService).add2(2);
System.out.println(testService.mapping.apply(2));
}
The test will return 4 instead of 6. Is this expected, or worth a bug report?
This is expected. Mockito creates a spy by making a shallow copy, and the method reference this::add2 gets copied over while keeping the reference to the old this.
TestService myTestService = new TestService();
TestService mySpy = Mockito.spy(myTestService);
In this example, mySpy is an instance of a generated subclass of TestService, which has all of its overridable methods overridden to delegate to Mockito, and all of its instance state shallow-copied from myTestService. This means that myTestService.mapping == mySpy.mapping, which also implies that the reference to this (meaning myTestService) is captured in the Function is copied over.
Method references applied to an instance capture that instance, as on the Oracle page on Method References under "Kinds of Method References". The object that receives the add2 call is the original, not the spy, so you get the original behavior (4) and not the spy-influenced behavior (6).
This should be somewhat intuitive: You can call the Function<Integer, Integer> without passing around a TestService instance, so it's pretty reasonable that the Function contains an implicit reference to a TestService implementation. You're seeing this behavior because the spy instance has its state copied from a real instance after the Function is initialized and this is stored.
Consider this alternative, which you could define on TestService:
BiFunction<TestService, Integer, Integer> mapping2 = TestService::add2;
Here, the function mapping2 doesn't apply to a particular object, but instead applies to any instance of TestService passed in. Consequently, your test would call this:
#Test
public void mockitoTest2(){
doReturn(6).when(testService).add2(2);
System.out.println(testService.mapping2.apply(testService, 2));
}
...and because you are passing in your spy testService, it will handle the virtual method call to add2 and invoke the behavior set on the spy (returning 6). There is no implicitly-saved this, so your function works as you'd expect.
See also: Mockito runnable: wanted but not invoked?

Mockito Test method parameters

I have started newly working on testing using mockito. I have 2 questions ...
1. Question
I have method like below with optional and must have parameter so when I call this service method without the must params it should throw Exception.
#RequestMapping( method=RequestMethod.GET, produces={"application/xml", "application/json"})
public ResponseEntity<PResponse> get(#RequestParam(value="params1",required=false) String params1,
#RequestParam(value ="params2",required=false) String params2,
#RequestParam(value= "params3",required=true) String params3,
#RequestParam(value="refresh",required=false) boolean refresh,
#RequestParam(value="params4",required=true) List<String> params4)
{method logic ...}
Here params1,2,refresh are optional and params3,4 are must so when i get request with out params3,4 it should give an error. I am trying to write a test for this using mockito
#Test(expected = RuntimeException.class)
public void throwExceptionIfMissingParams34() throws RuntimeException {
when(myService.get(argThat(new MessagesArgumentMatcher()))).thenThrow(new RuntimeException()) ;
}
I am getting error saying get() in myService can't be applied to expected Parameters:, Actual Arguments:
2. Question :
In the above get method I am calling other method which calls other service method to get data from DB
List<Product> lstProduct = productComponent.readProduct(params3,params4);
which calls
Product product = productReader.readProduct(params3, params4, Product.class);
where in ProductReader.java Service class it gets data from DB by running query. I am trying to test the
List lstProduct = productComponent.readProduct(params3,params4);
in get() method so I tried mocking the Service Object but getting NullPointer Exception when I run the test.
Ad 1. Question
#RequestParam is an annotation from Spring Framework. It's used to define parameters for Controllers. The annotation is used by Spring to map the web request params to arguments which your controller accepts. Testing this behaviour would be actually testing Spring itself. I wouldn't do that. Another thing is, are you really testing a Service, or rather a Controller?
Besides, Java doesn't have the possibility to invoke a method with different arguments than defined, the only possibility is to use varargs.
Ad. Question 2
You didn't specify form where you are getting the NPE. But a first guess would be that you didn't configure Mockito correctly. For example take a look at: NullPointerException in mockito unit test

Resources