Search code examples
javagroovyjunitmockingspock

How to mock HttpURLConnection and its responseCode in spock framework


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. 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.

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

    3. 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.