0

In my Java service, I use a library to interact with some APIs under the hood andget some response as shown below:

@Service
class CorrectionService {

  BookingDetails getData() {

    RequestClient <BookingDetails> requestClient = new RequestClient <> (new HttpClientFactory());
    ApiResponse <BookingDetails> apiResponse =
      requestClient.send(bookingRequest, BookingDetails.class);
    return apiResponse.getData();

  }

}

RequestClient is a library util class and I create an object of this class when the method correctionservice.getData() is called. Note that there are multiple such methods in the CorrectionService class, each of them creating different types of RequestClient objects. So, I can't autowire RequestClient in CorrectionService.

How can I mock apiResponse so that it will not actually call requestClient.send()

I tried the below mocking code, but it is still going inside the send() method of library and calling the actual APIs.

@InjectMocks
CorrectionService correctionService;

@Test
void test(){
  RequestClient requestClient = mock(RequestClient.class);
  ApiResponse apiResponse = ApiResponse.builder().build();
  when(requestClient.send(any(),any(Class.class))).thenReturn(apiResponse);
  assertAll(() -> correctionService.getData());
}
Rahul Raj
  • 3,197
  • 5
  • 35
  • 55
  • 1
    Does this answer your question? [Why are my mocked methods not called when executing a unit test?](https://stackoverflow.com/questions/74027324/why-are-my-mocked-methods-not-called-when-executing-a-unit-test) – knittl Jun 03 '23 at 19:04
  • As I pointed out in a comment on your previous question, you aren't using the mock you create. You need to refactor your code so that a RequestClient instance is injected into CorrectionService. – tgdavies Jun 04 '23 at 22:22
  • If you believe that you need to create different types of RequestClient, then inject a factory. – tgdavies Jun 05 '23 at 00:08

2 Answers2

0

Note that the library class was a generic.

In the service class, I had to create a bean for the library class like this:

@Bean("someName")
<T> RequestClient<T> getClient(){
   return new RequestClient<>();
}

And then use it in the service like:

ApiResponse<BookingDetails> apiResponse = (RequestClient<BookingDetails>)(applicationContext.getBean("someName")).send(bookingRequest, BookingDetails.class)

and then, in the test class, I was able to successfully mock the RequestClient with an INLINE mocking:

@Mock(mockMaker= MockMakers.INLINE)
RequestClient<?> requestClient;
Rahul Raj
  • 3,197
  • 5
  • 35
  • 55
0

I had previously overlooked the fact that in your service class you are newing up an instance of RequestClient in your service method. There is nothing that you can create in your test that will allow you to replace what's coming out of the new operator.

In general, you should not be "newing" up instances; it goes against the principles of IoC. You have two options if you want to get a mock here. You can either Autowire the RequestClient, or, as one of the comments points out, Autowire a Factory that produces a RequestClient. Here's an example for you to think over.

First, create a factory.

@Component
public class RequestClientFactory {

  public <T> RequestClient<T> createRequestClient() {
    return new RequestClient<T>(new HttpClientFactory());
  }
}

Inject this component into your service and use the factory method to get a new instance of RequestClient.

@Service
class CorrectionService {
  @Autowire
  private RequestClientFactory requestClientFactory;

  BookingDetails getData() {
    RequestClient <BookingDetails> requestClient = requestClientFactory.createRequestClient();
    ApiResponse <BookingDetails> apiResponse =
                requestClient.send(bookingRequest, BookingDetails.class);
    return apiResponse.getData();

  }
}

In your test, inject a mocked RequestClientFactory that returns your mocked RequestClient.

@InjectMocks
CorrectionService correctionService;

@Mock
private RequestClientFactory requestClientFactory;

@Test
void test(){
    RequestClient requestClient = mock(RequestClient.class);
    // Your factory has been injected into your service as a mock, 
    // so instruct the mocked factory to return your mocked 
    // RequestClient when createRequestClient is called.
    when(requestClientFactory.createRequestClient()).thenReturn(requestClient);
    ApiResponse apiResponse = ApiResponse.builder().build();
    when(requestClient.send(any(),any(Class.class))).thenReturn(apiResponse);

    // Now, inside this method when the factory is called, it's going to return your mocked RequestClient
    assertAll(() -> correctionService.getData());
}
lane.maxwell
  • 5,002
  • 1
  • 20
  • 30