[SOLVED] How to mock HttpURLConnection and its responseCode in spock framework

Issue

I am using Java and writing junit using spock framework in groovy, want to mock the HttpUrlConnection and set connection.getResponseCode() >> 200 as per different cases.

URL url = new URL(proxySettingDTO.getTestUrl());
HttpURLConnection connection = (HttpURLConnection) url.openConnection(proxy);
connection.setRequestMethod("GET");
connection.setUseCaches(false);
...
LOGGER.debug("Response code ::{} ",connection.getResponseCode()); //200 or 403

I have tried using

HttpURLConnection httpURLConnection = Mock()
URL url = new URL(proxySettingDTO.getTestUrl());
url.openConnection(_) >> httpURLConnection

But it is not working.

Solution

There are several things wrong in your question:

  1. It consists of an incoherent set of incomplete code snippets instead of an MCVE, i.e. a complete, minimal example which everybody who wants to help you can compile and run without doing your job and making up example classes by themselves. That is your job.

  2. In url.openConnection(_) >> httpURLConnection you are trying to stub a method result, but your URL object is not declared as a mock, stub or spy. I.e., your attempt is doomed to fail.

  3. Even if you would try to mock a URL, that JDK class is final, i.e. you cannot mock it, because mocks are subclasses.

  4. The class under test fetches the HttpURLConnection by calling url.openConnection(proxy). Because of (3), the method is not mockable, so you should externalise connection creation into a helper class ConnectionManager and then inject a mock instance into the class under test in order to make it testable.

In general tests are a design tool, not just for covering your code with tests. If testing is difficult, it means that the component design is too tightly coupled. Let the tests help drive your design using TDD (test-driven development) or at least test-driven refactoring, even though the latter is a bit late and means rework. If you decouple your components more, e.g. by not creating the object instances your class depends on internally but enabling API users to inject them, e.g. via constructors or setters, testability is much better and you have fewer headaches.

How about this?

class UnderTest {
  private Proxy proxy
  private ProxySettingDTO proxySettingDTO
  private ConnectionManager connectionManager
   UnderTest(Proxy proxy, ProxySettingDTO proxySettingDTO, ConnectionManager connectionManager) {
    this.proxy = proxy
    this.proxySettingDTO = proxySettingDTO
    this.connectionManager = connectionManager
  }

  int getConnectionResponseCode() {
    URL url = new URL(proxySettingDTO.getTestUrl())
    HttpURLConnection connection = (HttpURLConnection) connectionManager.openConnection(url, proxy)
    connection.setRequestMethod("GET")
    connection.setUseCaches(false)
    connection.getResponseCode()
  }
}
class ProxySettingDTO {
  String getTestUrl() {
    "https://scrum-master.de"
  }
}
class ConnectionManager {
  URLConnection openConnection(URL url, Proxy proxy) {
    url.openConnection(proxy)
  }
}
package de.scrum_master.stackoverflow.q71616286

import spock.lang.Specification

class HttpConnectionMockTest extends Specification {
  def test() {
    given: "a mock connection manager, returning a mock connection with a predefined response code"
    ConnectionManager connectionManager = Mock() {
      openConnection(_, _) >> Mock(HttpURLConnection) {
        getResponseCode() >> 200
      }
    }

    and: "an object under test using mock proxy, real DTO and mock connection manager"
    def underTest = new UnderTest(Mock(Proxy), new ProxySettingDTO(), connectionManager)

    expect: "method under test returns expected response"
    underTest.getConnectionResponseCode() == 200
  }
}

Try it in the Groovy web console.

Answered By – kriegaex

Answer Checked By – Marie Seifert (BugsFixing Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *