用Java模拟URL

我们在我们想要模拟的一个Java类中有一个URL对象,但它是一个最终类,所以我们不能。 我们不希望达到上述水平,并模拟InputStream,因为这仍然会给我们留下未经测试的代码(我们有严格的测试覆盖标准)。

我已经尝试了jMockIt的reflectionfunction,但我们在Mac上运行,并且Java代理处理程序存在一些我无法解决的问题。

那么在junit测试中是否有任何不涉及使用真实URL的解决方案?

当我有一个不能轻易模拟的类,因为它是最终的(或用C#封装)时,我通常的路径是在类周围编写一个包装器并使用包装器,无论我在哪里使用实际的类。 然后我会根据需要模拟包装类。

就像Rob说的那样,如果你想要的是模拟从URL返回的连接,你可以扩展URLStreamHandler 。 例如,使用mockito:

 final URLConnection mockUrlCon = mock(URLConnection.class); ByteArrayInputStream is = new ByteArrayInputStream( "".getBytes("UTF-8")); doReturn(is).when(mockUrlCon).getInputStream(); //make getLastModified() return first 10, then 11 when(mockUrlCon.getLastModified()).thenReturn((Long)10L, (Long)11L); URLStreamHandler stubUrlHandler = new URLStreamHandler() { @Override protected URLConnection openConnection(URL u) throws IOException { return mockUrlCon; } }; URL url = new URL("foo", "bar", 99, "/foobar", stubUrlHandler); doReturn(url).when(mockClassloader).getResource("pseudo-xml-path"); 

我使用了一个URLHandler,允许我从类路径加载一个URL。 所以以下

 new URL("resource:///foo").openStream() 

将从类路径中打开一个名为foo的文件。 为此,我使用公共实用程序库并注册处理程序。 要使用此处理程序,您只需要调用:

 com.healthmarketscience.common.util.resource.Handler.init(); 

并且资源URL现在可用。

我带着以下内容:

 public static URL getMockUrl(final String filename) throws IOException { final File file = new File("testdata/" + filename); assertTrue("Mock HTML File " + filename + " not found", file.exists()); final URLConnection mockConnection = Mockito.mock(URLConnection.class); given(mockConnection.getInputStream()).willReturn( new FileInputStream(file)); final URLStreamHandler handler = new URLStreamHandler() { @Override protected URLConnection openConnection(final URL arg0) throws IOException { return mockConnection; } }; final URL url = new URL("http://foo.bar", "foo.bar", 80, "", handler); return url; } 

这给了我一个包含我的模拟数据的真实URL对象。

我想你可以使用Powermock来做到这一点。 我最近能够使用PowerMock模拟URL类。 希望这可以帮助。

/ *实际class级* /

 import java.net.MalformedURLException; import java.net.URL; public class TestClass { public URL getUrl() throws MalformedURLException { URL url = new URL("http://localhost/"); return url; } } 

/ *测试类* /

 import java.net.URL; import junit.framework.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @RunWith(PowerMockRunner.class) @PrepareForTest(value = { TestClass.class }) public class TestClassTest { private TestClass testClass = new TestClass(); @Test public void shouldReturnUrl() throws Exception { URL url = PowerMockito.mock(URL.class); PowerMockito.whenNew(URL.class).withParameterTypes(String.class) .withArguments(Mockito.anyString()).thenReturn(url); URL url1 = testClass.getUrl(); Assert.assertNotNull(url1); } } 

如果您不想创建包装器:

注册URLStreamHandlerFactory

制作您想要公开的方法

嘲笑链条

 abstract public class AbstractPublicStreamHandler extends URLStreamHandler { @Override public URLConnection openConnection(URL url) throws IOException { return null; } } public class UrlTest { private URLStreamHandlerFactory urlStreamHandlerFactory; @Before public void setUp() throws Exception { urlStreamHandlerFactory = Mockito.mock(URLStreamHandlerFactory.class); URL.setURLStreamHandlerFactory(urlStreamHandlerFactory); } @Test public void should_return_mocked_url() throws Exception { // GIVEN AbstractPublicStreamHandler publicStreamHandler = Mockito.mock(AbstractPublicStreamHandler.class); Mockito.doReturn(publicStreamHandler).when(urlStreamHandlerFactory).createURLStreamHandler(Matchers.eq("http")); URLConnection mockedConnection = Mockito.mock(URLConnection.class); Mockito.doReturn(mockedConnection).when(publicStreamHandler).openConnection(Matchers.any(URL.class)); Mockito.doReturn(new ByteArrayInputStream("hello".getBytes("UTF-8"))).when(mockedConnection).getInputStream(); // WHEN URLConnection connection = new URL("http://localhost/").openConnection(); // THEN Assertions.assertThat(new MockUtil().isMock(connection)).isTrue(); Assertions.assertThat(IOUtils.toString(connection.getInputStream(), "UTF-8")).isEqualTo("hello"); } } 

PS:我不知道如何取消最后一行后的编号列表自动间隔

我会再次看看为什么模拟最终的数据对象。 因为根据定义,你不是在实际代码中inheritance对象,并且它不会是被测试的对象,所以你不需要对这些代码进行白盒测试; 只需传入适当的(真实的)URL对象,然后检查输出。

当难以创建适当的真实对象时,模拟对象很有用,或者真实对象的方法既耗时又依赖于某些有状态的外部资源(如数据库)。 在这种情况下,这些都不适用,所以我不明白为什么你不能只构造一个代表适当资源位置的真实URL对象。

JMockit确实允许你模拟像java.net.URL这样的最终JRE类。

似乎除了Sun之外的JDK 1.6的实现中可用的jdkDir / lib / tools.jar中的Attach API也不起作用。 我猜这些东西仍然太新/先进,或者根本没有得到其他JDK供应商(Apple,IBM与J9 JDK,Oracle与JRockit JDK)的必要关注。

因此,如果您在类路径中使用tools.jar遇到问题,请尝试使用“-javaagent:jmockit.jar”JVM参数。 它告诉JVM在启动时直接加载java代理,而不使用Attach API。 这应该适用于Apple JDK 1.5 / 1.6。

创建指向测试类本身的URL对象。

 final URL url = new URL("file://" + getClass().getProtectionDomain().getCodeSource().getLocation().getPath()); 

URL类是否实现了接口? 如果是这样,那么您可以使用控制反转或可配置工厂来实例化它,而不是直接构造,这将允许您在测试运行时注入/构造测试实例,而不是您当前拥有的最终实例。