Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to mock a URL connection

Hi I have a method that takes an URL as an input and determines if it is reachable. Heres the code for that:

public static boolean isUrlAccessible(final String urlToValidate) throws WAGNetworkException {
        URL url = null;
        HttpURLConnection huc = null;
        int responseCode = -1;
        try {
            url = new URL(urlToValidate);
            huc = (HttpURLConnection) url.openConnection();
            huc.setRequestMethod("HEAD");
            huc.connect();
            responseCode = huc.getResponseCode();
        } catch (final UnknownHostException e) {
            throw new WAGNetworkException(WAGConstants.INTERNET_CONNECTION_EXCEPTION);
        } catch (IOException e) {
            throw new WAGNetworkException(WAGConstants.INVALID_URL_EXCEPTION);
        } finally {
            if (huc != null) {
                huc.disconnect();
            }
        }
        return responseCode == 200;
    }

I want to unit test the isUrlAccessible() method using PowerMockito. I feel that I will need to use whenNew() to mock the creation of URL and the when url.openConnection() is called, return another mock HttpURLConnection object. But I have am not sure how to implement this? Am I on the right track? Can anyone help me in implementing this?

like image 358
Nemin Avatar asked Aug 15 '14 20:08

Nemin


4 Answers

Found the solution. First mock the URL class, then Mock the HttpURLConnection and when url.openconnection() is called, return this mocked HttpURLConnection object and finally set its response code to 200. Heres the code:

@Test
    public void function() throws Exception{
        RuleEngineUtil r = new RuleEngineUtil();
        URL u = PowerMockito.mock(URL.class);
        String url = "http://www.sdsgle.com";
        PowerMockito.whenNew(URL.class).withArguments(url).thenReturn(u);
        HttpURLConnection huc = PowerMockito.mock(HttpURLConnection.class);
        PowerMockito.when(u.openConnection()).thenReturn(huc);
        PowerMockito.when(huc.getResponseCode()).thenReturn(200);
        assertTrue(r.isUrlAccessible(url));

    }
like image 120
Nemin Avatar answered Nov 17 '22 13:11

Nemin


You can mock new Url instance with

whenNew(URL.class)..

Make sure you return a previously created mock object from that whenNew call.

URL mockUrl = Mockito.mock(URL.class);
whenNew(URL.class).....thenReturn(mockUrl );

Then you can add behavior to your mock as you want.

like image 30
Sezin Karli Avatar answered Nov 17 '22 14:11

Sezin Karli


Although this thread has some good suggestion, but if any of you is not interested to use those third-party libraries here is a quick solution.

public class MockHttpURLConnection extends HttpURLConnection {
    private int responseCode;
    private URL url;
    private InputStream inputStream;


    public MockHttpURLConnection(URL u){
        super(null);
        this.url=u;
    }
    @Override
    public int getResponseCode() {
        return responseCode;
    }


    public void setResponseCode(int responseCode) {
        this.responseCode = responseCode;
    }

    @Override
    public URL getURL() {
        return url;
    }

    public void setUrl(URL url) {
        this.url = url;
    }

    @Override
    public InputStream getInputStream() {
        return inputStream;
    }

    public void setInputStream(InputStream inputStream) {
        this.inputStream = inputStream;
    }

    @Override
    public void disconnect() {

    }

    @Override
    public boolean usingProxy() {
        return false;
    }

    @Override
    public void connect() throws IOException {

    }
}

And, this how you can set the desired behaviour

   MockHttpURLConnection httpURLConnection=new MockHttpURLConnection(new URL("my_fancy_url"));
        InputStream stream=new ByteArrayInputStream(json_response.getBytes());
        httpURLConnection.setInputStream(stream);
        httpURLConnection.setResponseCode(200);

Note: It just mock 3 methods from HttpUrlConnection and if you are using more method you need to make sure those are mock as well.

like image 3
minhaz Avatar answered Nov 17 '22 13:11

minhaz


URL is final class. To mock final class we can use PowerMockito with Junit. To mock final class we need to annotate Test class with @RunWith(PowerMockRunner.class) and @PrepareForTest({ URL.class })


@RunWith(PowerMockRunner.class) 
@PrepareForTest({ URL.class })
public class Test {
    @Test
    public void test() throws Exception {
        URL url = PowerMockito.mock(URL.class);
        HttpURLConnection huc = Mockito.mock(HttpURLConnection.class);
        PowerMockito.when(url.openConnection()).thenReturn(huc);
        assertTrue(url.openConnection() instanceof HttpURLConnection);
    }
}

But in the line PowerMockito.when(url.openConnection()).thenReturn(huc); following error is thrown:

java.lang.AbstractMethodError
    at java.net.URL.openConnection(URL.java:971)
    at java_net_URL$openConnection.call(Unknown Source) 

In order to get rid of this error we can modify our Test class as shown below:

@RunWith(PowerMockRunner.class) 
@PrepareForTest({ URL.class })
public class Test {
    @Test
    public void test() throws Exception {

        public class UrlWrapper {

            URL url;

            public UrlWrapper(String spec) throws MalformedURLException {
                url = new URL(spec);
            }

            public URLConnection openConnection() throws IOException {
                return url.openConnection();
            }
        }

        UrlWrapper url = Mockito.mock(UrlWrapper.class);
        HttpURLConnection huc = Mockito.mock(HttpURLConnection.class);
        PowerMockito.when(url.openConnection()).thenReturn(huc);
        assertTrue(url.openConnection() instanceof HttpURLConnection);
    }
}

Visit: https://programmingproblemsandsolutions.blogspot.com/2019/04/abstractmethoderror-is-thrown-on.html

like image 2
Pranab Thakuria Avatar answered Nov 17 '22 14:11

Pranab Thakuria