Mocking Record.insert() in jooq() - java

I have a controller end point that looks something like so
#POST
public JobComponentGetResponse createJobComponent(
#PathParam("jobId") Integer jobId,
#Valid JobComponentPostRequest request
) {
JobComponentRecord newJobComponent = dao.jobComponent.newRecord();
newJobComponent.setJobId(jobId);
newJobComponent.setLabel(request.label);
newJobComponent.setSqft(request.sqFt);
newJobComponent.insert();
return new JobComponentGetResponse(newJobComponent);
}
And a corresponding unit test
#Test
public void createJobComponent_createsAndReturnsTheDesiredRecord() {
JobComponentPostRequest request = new JobComponentPostRequest();
JobComponentRecord jobComponent = spy(new JobComponentRecord());
when(dao.jobComponent.newRecord()).thenReturn(jobComponent);
when(jobComponent.insert()).thenReturn(null);
JobComponentGetResponse response = jobComponentController.createJobComponent(jobId, request);
assertThat(response, samePropertyValuesAs(request));
}
I'm trying to test the response of my controller but I keep getting a null pointer exception when the controller calls newJobComponent.insert()
I've tried doing when(jobComponent.insert()).thenReturn(null); and that doesn't work either.

I suspect that your dao.jobComponent.newRecord() returns null even if you do : when(dao.jobComponent.newRecord()).thenReturn(jobComponent);
What is dao.jobComponent exactly and where does it comes from ?
EDIT : I don't have a full overview of your code but a getter for JobComponentRecord would be a good idea and you could easily determine his return value with mockito.

Related

Java unit test - exception not being thrown

Trying to write a test that will call my method, when that method makes a call to another method we will throw a custom exception i have made. Here i have simplified it all
2 functions
public MyJsonResponse hello() {
MyJsonResponse response = new MyJsonResponse();
response.setErrorMessage("1");
response.setStatus("some status");
response.setData("1");
response.setHttpResponse(200);
try{
hi();
return response;
}catch (MyServiceException e) {
response.setErrorMessage(e.getMessage());
response.setStatus("error creating");
response.setData("2");
response.setHttpResponse(e.getResponseStatus());
return response;
}
}
public String hi() throws MyServiceException{
LOG.error("Exception");
return "yea";
}
The test I have written is this
#Test
public void myTest() throws Exception {
given(service.hi()).willAnswer( invocation -> { throw new MyServiceException("abc msg",511); });
MyJsonResponse actual = service.hello();
Assert.assertNotNull(actual);
assertEquals(511, actual.getHttpResponse());
}
But unfortunately the result is as follows
java.lang.AssertionError:
Expected :511
Actual :200
Please, be sure that you are using a spy as you want to use the actual code for some methods of your mocked service and just stubbing specific methods of it. Please, see for instance this related SO question about the subject.
Also, consider modifying your test definition to use willThrow instead of willAnswer: as pointed out by #eis, you can still use the later, but the former is more straightforward.
Your code will look similar to this:
#Test
public void myTest() throws Exception {
MyService service = spy(MyService.class);
willThrow(new MyServiceException("abc msg",511))
.given(service)
.hi()
;
// As pointed out by #eis, you can still use willAnswer
// willAnswer(
// invocation -> { throw new MyServiceException("abc msg",511);}
// )
// .given(service)
// .hi()
// ;
MyJsonResponse actual = service.hello();
Assert.assertNotNull(actual);
assertEquals(511, actual.getHttpResponse());
}
regarding what you explain and what your code look like, I am not sure if I have well understood.
Thus, if you want that, your hi() : function throws an exception.
You have to make it first throws an exception. Take a look at code below!
public String hi() throws MyServiceException{
/*LOG.error("Exception");//No don't just log, throw a real exception as below*/
throw new MyServiceException("text here, if your constructor support it or nothing otherwise")
/*return "yea";//Nothing to return? we have just break the code by throwing the exception above*/
}
After that, please be very sure that your 'MyServiceException.getHttpResponse()' will really return 511
For this test to make sense, your hi() call should be done calling another service that you stub/mock in your test class. You're not doing that, so this approach won't work.
You wrote "the real method that hi represents does a lot", so it's about time you extract that to another service.

Spring Webflux Mockito - mock the response of a Webclient call

Small question regarding how to "force" or "mock" the response of a Webclient http call during unit test phase please.
I have a very straightforward method which does:
public String question() {
String result = getWebClient().mutate().baseUrl(someUrlVariable).build().post().uri("/test").retrieve().bodyToMono(String).block();
if (result == null) {
doSomething1();
}
if (result.equals("")) {
doSomething2();
}
if (result.equals("foo")) {
doSomething3();
}
As you can see, the complex part of this method is the Webclient call. It has (in this example) 7 .method() like .mutate(), .post(). etc...
In my use case, I am not that interested testing this Webclient, at all.
What I would like to have with Mockito, is somehow the equivalent of:
public String question() {
// it is just unit test. Mockito, please just return me the string I tell you to return please. Don't even execute this next line if possible, just return me this dummy response
String result = the-thing-I-tell-mockito-to-return;
if (result == null) {
doSomething1();
}
if (result.equals("")) {
doSomething2();
}
if (result.equals("foo")) {
doSomething3();
}
So far, I tried Mockito doNothing(), or Mockito.when(getWebclient()... ) of the entire line plus .thenReturn, but no luck.
How to achieve such please?
I would like to avoid those copy/pasting of when()
Well you have designed your code so that the only way to test it is by copy pasting of when.
So how have you designed it? well you have mixed API-code with logic which is something you should not do. The first thing you need to think about when writing tests is "What is it i want to test?" and the answer is usually Business logic.
If we look at your code:
public String question() {
// This is api code, we dont want to test this,
// spring has already tested this for us.
String result = getWebClient()
.mutate()
.baseUrl(someUrlVariable)
.build()
.post()
.uri("/test")
.retrieve()
.bodyToMono(String)
.block();
// This is logic, this is want we want to test
if (result == null) {
doSomething1();
}
if (result.equals("")) {
doSomething2();
}
if (result.equals("foo")) {
doSomething3();
}
}
When we design an application, we divide it into layers, usually a front facing api (RestController), then the business logic in the middle (Controllers) and lastly different resources that call other apis (repositories, resources etc.)
So when it comes to your application i would redesign it, split up the api and the logic:
#Bean
#Qualifier("questionsClient")
public WebClient webclient(WebClient.Builder webClient) {
return webClient.baseUrl("https://foobar.com")
.build();
}
// This class responsibility is to fetch, and do basic validation. Ensure
// That whatever is returned from its functions is a concrete value.
// Here you should handle things like basic validation and null.
#Controller
public class QuestionResource {
private final WebClient webClient;
public QuestionResource(#Qualifier("questionsClient") WebClient webClient) {
this.webClient = webClient;
}
public String get(String path) {
return webClient.post()
.uri(path)
.retrieve()
.bodyToMono(String)
.block();
}
}
// In this class we make business decisions on the values we have.
// If we get a "Foo" we do this. If we get a "Bar" we do this.
#Controller
public class QuestionHandler {
private final QuestionResource questionResource;
public QuestionResource(QuestionResource questionResource) {
this.questionResource = questionResource;
}
public String get() {
final String result = questionResource.get("/test");
// also i dont see how the response can be null.
// Null should never be considered a value and should not be let into the logic.
// Because imho. its a bomb. Anything that touches null will explode (NullPointerException).
// Null should be handled in the layer before.
if (result == null) {
return doSomething1();
}
if (result.equals("")) {
return doSomething2();
}
if (result.equals("foo")) {
return doSomething3();
}
}
}
Then in your test:
#Test
public void shouldDoSomething() {
final QuestionResource questionResourceMock = mock(QuestionResource.class);
when(questionResourceMock.get("/test")).thenReturn("");
final QuestionHandler questionHandler = new QuestionHandler(questionResourceMock);
final String something = questionHandler.get();
// ...
// assert etc. etc.
}
Also, i suggest you don't mutate webclients, create one webclient for each api because it gets messy fast.
This is written without an IDE, so there might be compile errors etc. etc.
You have to first ensure that getWebclient() returns a mock. Based on your existing code example I can't tell if that's coming for a different class or is a private method (it might make sense to inject the WebClient or WebClient.Builder via the constructor).
Next, you have to mock the whole method chain with Mockito. This includes almost copy/pasting your entire implementation:
when(webClient.mutate()).thenReturn(webClient);
when(webClient.baseUrl(yourUrl)).thenReturn(...);
// etc.
Mockito can return deep stubs (check the documentation and search for RETURN_DEEP_STUBS) that could simplify this stubbing setup.
However, A better solution would be to spawn a local HTTP server for your WebClient test and mock the HTTP responses. This involves less Mockito ceremony and also allows testing error scenarios (different HTTP responses, slow responses, etc.),

java.lang.AssertionError when executing unit test when using my own model as parameter

I got a AssertionError when I execute the unit test. response.getBody() is null.
Here is unit test method;
public void when_CustomerNumNotNull_Expect_TspResult() {
/*
some code
*/
TspResultDto tspResultDto = new TspResultDto();
tspResultDto.setTspResult("blabla");
Mockito.doReturn(tspResultDto).when(creditService)
.getTspResult(tspInputDto);
ResponseEntity<TspResponse> response = creditController
.getTspResult(TspRequest);
Assert.assertNotNull(response.getBody()); // error occured this line because body null.
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
//Assert.assertEquals(true, response.getBody().isHrResult());
}
}
TspInputDto and TspRequest are my model Class.
But I don't get an error when I run it with a single parameter like below without needing the model class.
Mockito.doReturn(newCreditApplicationDto).when(creditService)
.getNewCreditApplicationNo(customerNum);
ResponseEntity<NewCreditApplicationResponse> response = creditController
.getNewCreditApplicationNo(customerNum);
Assert.assertNotNull(response.getBody());
Assert.assertEquals(HttpStatus.OK, response.getStatusCode());
Here is controller;
public ResponseEntity<TspResponse> getTspResult(
TspRequest tspRequest) {
TspInputDto tspInputDto = creditDemandRestMapper
.toTspInputDto(tspRequest);
TspResultDto tspResultDto = creditService
.getTspResult(tspInputDto);
TspResponse tspResponse = creditDemandRestMapper
.toTspResponse(tspResultDto);
return new ResponseEntity<>(tspResponse, HttpStatus.OK);
}
Here is service;
public TspResultDto getTspResult(
TspInputDto tspInputDto) {
TspResultDto tspResultDto = new TspResultDto();
/*
Some code here...
*/
return tspResultDto;
}
Where am I doing wrong?
In this line:
Mockito.doReturn(tspResultDto).when(creditService)
.getTspResult(tspInputDto);
you set the mock to return your tspResultDto object if the argument equals() to tspInputDto, meaning it should be an equal object (in terms of equals() comparison). Probably your TspInputDto class doesn't have equals method defined properly.
Anyways, I'd propose to rewrite this line of code using argThat matcher:
Mockito.doReturn(tspResultDto).when(creditService)
.getTspResult(ArgumentMatchers.argThat(tspInputDto ->
// condition for provided tspInputDto
));
Have you mocked the creditController? If you haven't then it will not give you response mock object.

Rx onErrorReturn is not called when test case run

I have
public Single<APIResponse> getPayment(Identity identity, String id) {
return client.getPayment(identity, id).onErrorReturn(__ -> getTheSameEntityIfError(id));
}
client is a web-service client which call external service
In case of any error returned from client.getPayment, then it will call getTheSameEntityIfError(id)
I am trying to write a test case to cover this method
#RunWith(MockitoJUnitRunner.class)
public class AdapterTest {
#Mock
PaymentsClient client;
#InjectMocks
Adapter adapter;
Identity identity = testIdentity();
PaymentEntity payment = testPayment();
#Test
public void getPayment() {
when(client.getPayment(any(), any())).thenThrow(new NotFoundException());
APIResponse apiResponse = adapter.getPayment(identity, "id").blockingGet();
assertThat(payment.getId(), equalTo(apiResponse.getId(1)));
}
}
I this case i want to simulate if the external service client.getPayment() returns an error, then call getTheSameEntityIfError(id).
I expect the onErrorReturn operation will be called, but it always throw NotFoundException and the onErrorReturn never called.
Any idea what is wrong ?
You mock client.getPayment() to throw instead of returning something RxJava can work with, thus RxJava doesn't even get involved in this case. Make client.getPayment return a Single.error().

Getting NullpointerException in Mockito

I have actual method below:
public ResponseEntity<Message> catEnter(#PathVariable("catId") BigInteger catId, #RequestBody Catrequest catReq, HttpServletRequest request) throws CatDataException, InvalidCatExcecatption {
Message message = new Message();
try {
message = catManager.submitData(catReq.getMessage(), catId, request);
} catch (IOException e) {
throw new CatDataAppException(e.getMessage());
}
return (ResponseEntity<Message>) restResponse(message, request.getMethod());
// Getting null pointer exception in above line
}
I am using mockito for my test code as below:
#Test
public void submitData() throws Exception {
Message mes = new Message();
mes.setCode("00");
mes.setMessage("hi");
ResponseEntity<Message> responseentity = ((ResponseEntity<Message>) catController.catEnter(BigInteger.valueOf(3431), catRequest, mockRequest));
}
I'm getting null pointer exception, Message going as a null, even I set the value explicitly?
Here:
You pass mockRequest when making that call to your production code:
ResponseEntity<Message> responseentity = ... catController.catEnter(... mockRequest));
And your production call does:
return (ResponseEntity<Message>) restResponse(message, request.getMethod());
So the only conclussion: mockRequest is null!
So, first make sure that the passed variable is not null; like:
Request mockedRequest = mock(Request.class);
Or, use the #Mock annotation in case that mockedRequest is a field in your test class.
On top of that; you probably want to do some mock-specification, like:
when(mockedRequest.getMethod()).thenReturn( whatever )
But beyond that, you are lacking a lot of the fundamental basics of Java:
naming conventions: variable names go camelCase, like entityResponse. And typically, tests are named like testCatEnter to express the method that is tested.
You have casts ... where they are not required.
You have quite some code there ... that is unused, like the mes declaration in your test method.
Long story short: I have the feeling that you are overburdening yourself dramatically. First learn the basics; then go for the advanced Mockito stuff.

Categories