0

I have an interface defined as follows:

public interface HttpClient {
    public <T> UdemyResponse<T> get(Request request,
      JSONUnmarshaler<T> unmarshaller, Gson gson)
      throws UdemyException, IOException;
}

I have a class that implements the interface:

public class OkHttp implements HttpClient {
public OkHttpClient client;

final Logger logger = LoggerFactory.getLogger(getClass());

public OkHttp() {
    this.client = new OkHttpClient();
}

@Override
public <T> UdemyResponse<T> get(Request request, JSONUnmarshaler<T> unmarshaller, Gson gson)
        throws UdemyException, IOException {

int status_code = 0;
        String next = null;
        String rawJSON = null;
        JsonElement jsonelement = null;

    Boolean retry = true;
    int attempts = 3;

    while ((attempts >= 0) && (retry) && status_code != 200) {
        try {
            Response response = this.client.newCall(request).execute();

            rawJSON = response.body().string();

            jsonelement = gson.fromJson(rawJSON, JsonElement.class);

            next = gson.fromJson(jsonelement.getAsJsonObject().get("next"), String.class);

            status_code = response.code();

            if (status_code == 401) {
                try {
                    logger.warn("token expired");
                    TimeUnit.SECONDS.sleep(5);
                    retry = true;
                    continue;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            if ((status_code / 100) == 5) {
                logger.warn("gateway error");
                retry = true;
                continue;
            }

        } catch (IOException e) {
            e.printStackTrace();
            // this exception will be propagated to the main method and handled there to exit the program,
            // this exception should end the program.
            throw e;

        }

        attempts -= 1;
        retry = false;

    }   
    if (status_code != 200) {
        throw new UdemyException();
    }

    return new UdemyResponse<T>(status_code, next, rawJSON,
            unmarshaller.fromJSON(gson, jsonelement.getAsJsonObject()));

}

If I mock my interface I can write test cases for get() method but my get() method uses the this.client and I need to mock that object as well.

In this case, is it better to mock the OkHttp object rather than the interface?

user1050619
  • 19,822
  • 85
  • 237
  • 413
  • Possible duplicate of [What is dependency injection?](https://stackoverflow.com/questions/130794/what-is-dependency-injection) – luk2302 Jan 31 '19 at 17:11
  • 1
    `If I mock my interface I can write test cases for get() method` - this statement is confusing, if you wanna test the get() method of OkHttp, you don't mock the OkHttp. Yes as @luk2302 suggested use constructor based DI to mock OkHttpClient & test OkHttp. – Amith Kumar Jan 31 '19 at 17:15
  • @AmithKumar the get method uses the this.client object to call the api, I basically need to mock the client object so that I can mock its responses and not mock the interface and its get method..DI looks a good solution – user1050619 Jan 31 '19 at 17:42
  • So constructor based DI should solve your problem statement. – Amith Kumar Jan 31 '19 at 17:45

2 Answers2

0

If you are attempting to test get() then you should not mock that method, if you do, what is it that you are testing? You need to mock the other dependencies of get() to help you test it in isolation. In this case if this.client is a dependency of get(), this is what you need to mock.

Kevin Hooke
  • 2,583
  • 2
  • 19
  • 33
0

Edited in response to question changes

This is terrible: (status_code / 100). Test for the real status code there.

You should do the following:

  1. Create a mock OkHttpClient.
  2. Inject the mock into your test class using reflection.
  3. test the get method.

You may want to change the mocking of the ok thing in the code below, but you should be able to just use simple Mockito mocks for everything. Here is some example code:

public class TestOkHttp
{
  private static final String VALUE_JSON_STRING "some JSON string for your test";

  private OkHttp classToTest;

  @Mock
  private ClassWithExecute mockClassWithExecute;

  @Mock
  private OkHttpClient mockOkHttpClient;

  @Mock
  private Response mockResponse;

  @Mock
  private ResponseBodyClass mockResponseBodyClass;

  @Mock
  private Request mockRequest;

  private Gson testGson;

  @Test
  public void get_describeTheTest_expectedResults()
  {
    final JSONUnmarshaler<someclass> unmarshallerForThisTest = new JSONUnmarshaler<>()

    // setup the mocking functionality for this test.
    doReturn(desiredStatusCode).when(mockResponse).code();


    classToTest.get()
  }

  @Before
  public void preTestSetup()
  {
    MockitoAnnotations.initMocks(this);

    classToTest = new OkHttp();

    testGson = new Gson();

    doReturn(mockResponse).when(mockClassWithExecute).execute();

    doReturn(mockClassWithExecute).when(mockOkHttpClient).newCall(mockRequest);

    doReturn(mockResponseBodyClass).when(mockResponse).body();

    doReturn(VALUE_JSON_STRING).when(mockResponseBodyClass).string();

    ReflectionTestUtils.setField(classToTest,
      "client",
      mockOkHttpClient);
  }
}
DwB
  • 37,124
  • 11
  • 56
  • 82
  • I removed the implementation details to keep it simple...I have added it now to be clear..The get method is using the this.client to retrieve the data by api calls..Now, I want to test the get() method by mocking the this.client(mock the http call)..So, in this case I don't have to mock the interface instead mock the http client(After making it DI) and then test it.. – user1050619 Jan 31 '19 at 21:22
  • https://github.com/gmazzo/okhttp-client-mock , planning to mock the client using this library.. – user1050619 Jan 31 '19 at 21:28