【问题标题】:how to mock a URL connection如何模拟 URL 连接
【发布时间】:2014-08-15 20:59:02
【问题描述】:

您好,我有一个方法可以将 URL 作为输入并确定它是否可访问。 代码如下:

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;
    }

我想使用PowerMockito 对 isUrlAccessible() 方法进行单元测试。我觉得我需要使用whenNew() 来模拟URL 的创建,并且在调用url.openConnection() 时,返回另一个模拟HttpURLConnection 对象。但我不确定如何实现这一点?我在正确的轨道上吗?谁能帮我实现这个?

【问题讨论】:

  • 对于它的价值,我建议你仔细看看 JMockit (jmockit.github.io),然后看看你是否可以拒绝它——我不能,它一直是我的从那时起就开始使用模拟框架。

标签: java junit mocking powermock jmockit


【解决方案1】:

找到了解决办法。首先模拟 URL 类,然后模拟 HttpURLConnection,当调用 url.openconnection() 时,返回这个模拟的 HttpURLConnection 对象,最后将其响应代码设置为 200。代码如下:

@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));

    }

【讨论】:

  • 这行得通吗,它在PowerMockito.when(u.openConnection()).thenReturn(huc); 行给我一个错误,说AbstractMethodError。
  • 对我也不起作用...我得到:java.net.MalformedURLException: no protocol
  • 对于模拟 URL,您还应该考虑添加 @PrepareForTest({URL.class, }) 按照vaguehope.com/2012/02/powermock-puzzler
  • 对于将来遇到 AbstractMethodError 的其他人,我认为解决方案可能是将您的 @Test 导入从 org.junit.jupiter.api.Test 更改为 org.junit.Test。
【解决方案2】:

你可以模拟新的 Url 实例

whenNew(URL.class)..

确保从 whenNew 调用中返回以前创建的模拟对象。

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

然后您可以根据需要向您的模拟添加行为。

【讨论】:

  • 我们不能模拟最终课程(URL)
  • @doN,Powermockito 可以模拟最终类(URL)。
【解决方案3】:

URL 是最终类。要模拟最终课程,我们可以将 PowerMockito 与 Junit 一起使用。 要模拟最终类,我们需要使用 @RunWith(PowerMockRunner.class) 和 @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);
    }
}

但在 PowerMockito.when(url.openConnection()).thenReturn(huc); 行中抛出以下错误:

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

为了摆脱这个错误,我们可以修改我们的Test类,如下所示:

@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);
    }
}

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

【讨论】:

    【解决方案4】:

    虽然这个帖子有一些很好的建议,但是如果你们中的任何人对使用这些第三方库不感兴趣,这里是一个快速的解决方案。

    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 {
    
        }
    }
    

    而且,您可以通过这种方式设置所需的行为

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

    注意:它只是模拟来自HttpUrlConnection 的 3 个方法,如果您使用更多方法,您需要确保这些方法也是模拟的。

    【讨论】:

      【解决方案5】:

      为了通过mockito库模拟java.net.URL类,你需要执行以下步骤:

      • 在 src/tests/resources 目录中创建一个名为“mockito-extensions”的目录。
      • 在文件夹中创建一个文本文件,名为 org.mockito.plugins.MockMaker 并将mock-maker-inline 文本放入文件中。
      • 您可以像下面这样模拟该类:

      代码:

      package myproject;
      
      import org.junit.Test;
      
      import java.net.HttpURLConnection;
      import java.net.URL;
      import static org.junit.Assert.*;
      import static org.mockito.Mockito.*;
      
      public class Test {
          @Test
          public void test() throws Exception {
              URL url = mock(URL.class);
              HttpURLConnection huc = mock(HttpURLConnection.class);
              when(url.openConnection()).thenReturn(huc);
              assertTrue(url.openConnection() instanceof HttpURLConnection);
          }
      }
      

      【讨论】:

        【解决方案6】:

        使用 JMockit 模拟 API 更简单(没有模拟甚至更简单):

        import java.io.*;
        import java.net.*;
        import org.junit.*;
        import static org.junit.Assert.*;
        import mockit.*;
        
        public final class ExampleURLTest {
           public static final class ClassUnderTest {
              public static boolean isUrlAccessible(String urlToValidate) throws IOException {
                 HttpURLConnection huc = null;
                 int responseCode;
        
                 try {
                    URL url = new URL(urlToValidate);
                    huc = (HttpURLConnection) url.openConnection();
                    huc.setRequestMethod("HEAD");
                    huc.connect();
                    responseCode = huc.getResponseCode();
                 }
                 finally {
                    if (huc != null) {
                       huc.disconnect();
                    }
                 }
        
                 return responseCode == 200;
              }
           }
        
           // Proper tests, no unnecessary mocking ///////////////////////////////////////
        
           @Test
           public void checkAccessibleUrl() throws Exception {
              boolean accessible = ClassUnderTest.isUrlAccessible("http://google.com");
        
              assertTrue(accessible);
           }
        
           @Test(expected = UnknownHostException.class)
           public void checkInaccessibleUrl() throws Exception {
              ClassUnderTest.isUrlAccessible("http://inaccessible12345.com");
           }
        
           @Test
           public void checkUrlWhichReturnsUnexpectedResponseCode(
              @Mocked URL anyURL, @Mocked HttpURLConnection mockConn
           ) throws Exception {
              new Expectations() {{ mockConn.getResponseCode(); result = -1; }};
        
              boolean accessible = ClassUnderTest.isUrlAccessible("http://invalidResource.com");
        
              assertFalse(accessible);
           }
        
           // Lame tests with unnecessary mocking ////////////////////////////////////////
        
           @Test
           public void checkAccessibleUrl_withUnnecessaryMocking(
              @Mocked URL anyURL, @Mocked HttpURLConnection mockConn
           ) throws Exception {
              new Expectations() {{ mockConn.getResponseCode(); result = 200; }};
        
              boolean accessible = ClassUnderTest.isUrlAccessible("http://google.com");
        
              assertTrue(accessible);
           }
        
           @Test(expected = UnknownHostException.class)
           public void checkInaccessibleUrl_withUnnecessaryMocking(
              @Mocked URL anyURL, @Mocked HttpURLConnection mockConn
           ) throws Exception {
              new Expectations() {{ mockConn.connect(); result = new UnknownHostException(); }};
        
              ClassUnderTest.isUrlAccessible("http://inaccessible12345.com");
           }
        }
        

        (在 JDK 8 和 9 上使用 JMockit 1.47 验证。)

        【讨论】:

          【解决方案7】:

          您需要先配置Mockito,然后才能使用它来模拟最终的类和方法。 (使用 JUnit 5 和mockito-core-3.6.28 测试)

          步骤:

          • 创建一个名为src/test/resources/mockito-extensions的目录
          • 在其中创建一个名为org.mockito.plugins.MockMaker的文件
          • 在文件中添加一行,mock-maker-inline

          现在,您可以模拟您的最终课程,例如:

          URL url = Mockito.mock(URL.class)
          

          注意:根据issue,PowerMock 不适用于 JUnit 5。

          【讨论】:

            猜你喜欢
            • 2020-07-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-11-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多