Hot questions for Using Mockito in web services

Top 10 Java Open Source / Mockito / web services

Question:

I have written following code to publish some metrics around AWS Step function (its java lambda for aws)

@Override
public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException {
    int inProgressStateMachines = 0;

    LocalDateTime now = LocalDateTime.now();

    long alarmThreshold = getAlarmThreshold(input, context.getLogger());

    AWSStepFunctions awsStepFunctions = AWSStepFunctionsClientBuilder.standard().build();

    ListStateMachinesRequest listStateMachinesRequest = new ListStateMachinesRequest();
    ListStateMachinesResult listStateMachinesResult = awsStepFunctions.listStateMachines(listStateMachinesRequest);

    for (StateMachineListItem stateMachineListItem : listStateMachinesResult.getStateMachines()) {

        ListExecutionsRequest listExecutionRequest = new ListExecutionsRequest()
                .withStateMachineArn(stateMachineListItem.getStateMachineArn())
                .withStatusFilter(ExecutionStatus.RUNNING);

        ListExecutionsResult listExecutionsResult = awsStepFunctions.listExecutions(listExecutionRequest);

        for (ExecutionListItem executionListItem : listExecutionsResult.getExecutions()) {

            LocalDateTime stateMachineStartTime = LocalDateTime.ofInstant(
                    executionListItem.getStartDate().toInstant(), ZoneId.systemDefault());

            long elapsedTime = ChronoUnit.SECONDS.between(stateMachineStartTime, now);

            if (elapsedTime > alarmThreshold){
                inProgressStateMachines++;
            }
        }

        publishMetrics(inProgressStateMachines);
    }
}

Now I am trying to unit-test this method and having some issues.

First of all, I get error that Mockito can not mock final class when i tried to mock AWSStepFunctionsClientBuilder.

Secondly, I have private methods which are being called with specific params.

The question is

  1. How can i unit test this code? I read it somewhere that if a code isn't unit-testable then its a bad design. How can i improve this code so that its easily testable? I would prefer to keep those helper methods as private methods.

  2. How can i mock final objects from AWS SDK to test this code? I can not use any other framework but Mockito.


Answer:

You actually don't want to mock AWSStepFunctionsClientBuilder because you are actually calling AWSStepFunctions, which you'll have to mock anyway even after mocking the builder.

So make AWSStepFunctions an instance variable:

// add appropriate getter/setter as well
private AWSStepFunctions awsStepFunctions;

Where you currently call the builder to initialize awsStepFunctions, change to:

if (awsStepFunctions == null)
    awsStepFunctions = AWSStepFunctionsClientBuilder.standard().build();

Now, during unit test, you can set awsStepFunctions to a mocked instance, bypassing the conditional initialization above.

[Edit] Some more thoughts based on @kdgregory's comment below:

The answer above is meant to provide a solution given the existing code structure, without requiring any major refactoring. In general though, ideally you would want to move the bulk of the code into another plain, more testable Java class, where you can properly inject dependencies, manage life cycles, etc.

Question:

I am working in AWS(Amazon web service) , mockito and java junit4 environment. In my class I am using one method which takes Request object as a parameter and depending on that object I am getting response. Following is my code,

private Response<String> getStringResponse(Request<?> request) {
        try {
            AmazonHttpClient client = new AmazonHttpClient(new ClientConfiguration());
            ExecutionContext executionContext = new ExecutionContext(true);
            HttpResponseHandler<AmazonClientException> handler = getErrorResponseHandler();
            HttpResponseHandler<String> responseHandler = getHttpResponseHandler();
            RequestExecutionBuilder requestExecutionBuilder = client.requestExecutionBuilder();
            requestExecutionBuilder = requestExecutionBuilder.executionContext(executionContext);
            requestExecutionBuilder = requestExecutionBuilder.request(request);
            requestExecutionBuilder = requestExecutionBuilder.errorResponseHandler(handler);
            Response<String> response = requestExecutionBuilder.execute(responseHandler);
            return response;
        } catch (Exception e) {
            AppLogger.getLogger().error("Exception in :: classname :: getStringResponse() ::");
            throw e;
        }
    }

What I want to do is, I want to mock this whole scenario means on whatever request, my method should give me the custom response object which I want, irrespective of what Request is coming. I am calling this method from my junit test. So is there anyway to do this?


Answer:

Before you can test this method with JUnit and Mockito, you should write a clean and testable codes. Importantly, you have to remove all dependencies inside method and initialize them from outside. For example,

 private Response<String> getStringResponse(Request<?> request,
      AmazonHttpClient client,
      ExecutionContext executionContext,
      HttpResponseHandler<AmazonClientException> handler,
      HttpResponseHandler<String> responseHandler) {
    try {
      RequestExecutionBuilder requestExecutionBuilder = client.requestExecutionBuilder()
          .executionContext(executionContext)
          .request(request)
          .errorResponseHandler(handler);
      Response<String> response = requestExecutionBuilder.execute(responseHandler);
      return response;
    } catch (Exception e) {
      AppLogger.getLogger().error("Exception in :: classname :: getStringResponse() ::");
      throw e;
    }
  }

Now you can test it by mocking these above dependencies.

  @Mock
  AmazonHttpClient client;
  @Mock
  ExecutionContext executionContext;
  @Mock
  HttpResponseHandler<AmazonClientException> handler;
  @Mock    
  HttpResponseHandler<String> responseHandler;

  // For request, you can create a custom one or use mock data
  Request<?> request;

  @Before
  public void setUp() {
    MockitoAnnotations.initMocks(this);
    // other setups
  }

  @Test
  public void getStringResponseTest() {
    // you can test now
    yourCall.getStringResponse(request, client, executionContext, handler, responseHandler);
    // verify whatever you want....
  }