Saturday, February 28, 2015

Mocking java.net.URL


Introduction.

Mocking is a crucial part of the unit testing. It gives power to the developer to push the limits of the logic without pleasing the dependencies of the logic. Mocking has it's own forms for different testing paradigms and also limitations. Recently I have used mockito mock library to develop some unit test cases. Even though Mokito is grate tool for mocking it has few limitations ,by design Mokito can not mock/spy final classes, anonymous classes or primitive types. Here I will explain how I have found a workaround for final class problem.

Problem
Have a look of "public String getContent(URL url) throws IOException" method which takes java.net.URL as an argument and read the input stream from the http connection. Here I need to mock java.net.URL is a final class we often see in our codes , because of above limitation we discussed above I can not get HttpURLConnection form mocked URL instance.


     public class WebContentManager {

    private final String USER_AGENT = "Mozilla/5.0";
   
    public String getContent(URL url) throws IOException {
   
     HttpURLConnection httpConnection = null;
     BufferedReader in = null;
     StringBuffer response = new StringBuffer();
   
     try {
      httpConnection = (HttpURLConnection) url.openConnection();
      httpConnection.setRequestMethod("GET");
      httpConnection.setRequestProperty("User-Agent", USER_AGENT);
   
      // int responseCode = httpConnection.getResponseCode();
   
      in = new BufferedReader(new InputStreamReader(
        httpConnection.getInputStream()));
      String inputLine;
   
      while ((inputLine = in.readLine()) != null) {
       response.append(inputLine);
      }
     } finally {
   
      try {
       in.close();
      } catch (IOException ex) {
   
      }
      if (httpConnection != null) {
       httpConnection.disconnect();
      }
     }
   
     return response.toString();
    }
    
   }
   
Solution
So I started to dig in to other ways to create mock URL instance with injected mock HttpURLConnection. java.net.URL class has constructor which takes java.net.URLStreamHandler as an argument. java.net.URLStreamHandler is an interface with the method  protected URLConnection openConnection(URL u ) throws IOException.So if I can inject the mocked HttpURLConnection in to URLStreamHandler openConnection() method.
Here goes the unit test implementation.
   public class WebContentManagerTest {

    String response;
    
    @Before
    public void setUp(){
     response = "";
    }
    
    @Test
    public void testGetContent() throws IOException {
     
          final HttpURLConnection mockCon = mock(HttpURLConnection. class);
          InputStream inputStrm = IOUtils.toInputStream( response);       
          when(mockCon.getLastModified()).thenReturn((Long)10L, (Long)11L);
          when(mockCon.getInputStream()).thenReturn( inputStrm);
          
           //mocking httpconnection by URLStreamHandler since we can not mock URL class.
          URLStreamHandler stubURLStreamHandler = new URLStreamHandler() {
              @Override
               protected URLConnection openConnection(URL u ) throws IOException {
                  return mockCon ;
               }           
          };
          
          WebContentManager wcm = new WebContentManager();
       URL url = new URL(null,"https://www.google.com",stubURLStreamHandler);
          String actual = wcm.getContent(url);
          assertEquals(response, actual);
   
    }

    }
   

Conclusion
When you find final class that can not mock , try to find alternative ways to create the object and inject what you wanted in to that. 




    




2 comments:

  1. Thanks for this post. It worked for me like a charm!

    ReplyDelete
  2. can you help me here http://stackoverflow.com/questions/39031175/testing-powermock-simulate-http-server-time-out-for-client-call?noredirect=1#comment65533595_39031175

    ReplyDelete