在Java中模拟URL
我们想模拟其中一个Java类中的URL对象,但它是最后一个类,所以我们不能。我们不想达到更高的级别,并模拟InputStream,因为这仍然会留下未经测试的代码(我们有严格的测试覆盖率标准) 我尝试过jMockIt的反射功能,但我们在Mac上工作,Java代理处理程序存在一些问题,我还没有解决在Java中模拟URL,java,url,mocking,Java,Url,Mocking,我们想模拟其中一个Java类中的URL对象,但它是最后一个类,所以我们不能。我们不想达到更高的级别,并模拟InputStream,因为这仍然会留下未经测试的代码(我们有严格的测试覆盖率标准) 我尝试过jMockIt的反射功能,但我们在Mac上工作,Java代理处理程序存在一些问题,我还没有解决 那么,有什么解决方案不涉及在junit测试中使用真实URL吗?当我有一个类,因为它是最终的(或用C#密封),所以不容易被模仿时,我通常的方法是围绕该类编写一个包装器,并在我使用实际类的任何地方使用该包装器
那么,有什么解决方案不涉及在junit测试中使用真实URL吗?当我有一个类,因为它是最终的(或用C#密封),所以不容易被模仿时,我通常的方法是围绕该类编写一个包装器,并在我使用实际类的任何地方使用该包装器。然后根据需要模拟包装器类。我使用了一个URLHandler,它允许我从类路径加载URL。那么下面
new URL("resource:///foo").openStream()
将从类路径中打开名为foo的文件。要做到这一点,我使用一个函数并注册一个处理程序。要使用此处理程序,只需调用:
com.healthmarketscience.common.util.resource.Handler.init();
资源URL现在可用。我将再次了解您为什么要模拟最终的数据对象。因为根据定义,您在实际代码中没有对对象进行子类化,而且它也不会是被测试的对象,所以您不需要对该代码进行白盒测试;只要传入任何合适的(真实的)URL对象,并检查输出
当很难创建合适的真实对象,或者真实对象的方法很耗时或者依赖于某个有状态的外部资源(如数据库)时,模拟对象非常有用。这两种方法都不适用于本例,因此我不明白为什么不能构造一个表示适当资源位置的真实URL对象。URL类实现接口了吗?如果是这样,那么您可以使用控制反转或可配置工厂来实例化它,而不是直接构造,这将允许您在测试运行时注入/构造测试实例,而不是当前拥有的最终实例。JMockit确实允许您模拟最终的JRE类,如java.net.URL 除了Sun之外,JDK1.6实现中的jdkDir/lib/tools.jar中的Attach API似乎也不起作用。我猜这些东西还是太新/太高级了,或者根本没有得到其他JDK供应商(苹果、IBM和J9JDK、Oracle和JRockit JDK)的必要关注
因此,如果您在类路径中使用tools.jar时遇到问题,请尝试使用“-javaagent:jmockit.jar”JVM参数。它告诉JVM在启动时直接加载java代理,而不使用附加API。这应该在Apple JDK 1.5/1.6中起作用。正如Rob所说,如果您想要模拟从URL返回的连接,您可以扩展
URLStreamHandler
。例如,对于mockito:
final URLConnection mockUrlCon = mock(URLConnection.class);
ByteArrayInputStream is = new ByteArrayInputStream(
"<myList></myList>".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");
final URLConnection mockUrlCon=mock(URLConnection.class);
ByteArrayInputStream是=新的ByteArrayInputStream(
.getBytes(“UTF-8”);
doReturn(is).when(mockUrlCon).getInputStream();
//使getLastModified()先返回10,然后返回11
当(mockUrlCon.getLastModified())。然后返回((长)10L,(长)11L);
URLStreamHandler stubUrlHandler=新的URLStreamHandler(){
@凌驾
受保护的URL连接openConnection(URL u)引发IOException{
返回mockUrlCon;
}
};
URL URL=新URL(“foo”、“bar”、99、/foobar”、stubUrlHandler);
doReturn(url).when(mockClassloader).getResource(“伪xml路径”);
我同意以下观点:
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类。希望这有帮助 /*实际类*/
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:我不知道如何取消最后一行后的编号列表自动间距创建一个指向测试类本身的URL对象
final URL url =
new URL("file://" + getClass().getProtectionDomain().getCodeSource().getLocation().getPath());
是的,但是我需要对包装器进行单元测试,并在其中包含URL类,问题仍然存在。否。包装器非常薄,只反映URL上的方法。你不需要对它进行单元测试,你应该能够通过检查来验证它。嗯,是的,这是真的。正如我上面所说的,这是因为我们有非常严格的覆盖标准,我需要从我们的覆盖范围中去掉包装。但这是我们最初提出的解决方案,我想这是有道理的。谢谢“相当严格的覆盖率标准”-代码覆盖率是一个负指标,这意味着它只在你没有覆盖率的情况下告诉你有用的信息。一旦获得覆盖率,它就不会真正告诉您关于代码质量的任何信息。也就是说,覆盖范围并不表示代码经过良好测试。如果可以的话,我建议你尝试改变严格的标准。这被标记为一个公认的答案,有没有一个例子说明你是如何编写包装并测试包装而不是
新URL
?当然,这是希望将测试与任何类型的真实URL分离,当我们调用openConnection时,我们打开了一个新的依赖项。我认为这是一个合法的问题。模拟框架不应该隐藏这些东西……不,正如我上面所说,URL是最后一个类,它来自JDK。好的,我把问题理解为“我们有一个由最后一个类实现的URL对象(比如MyURL,据我所知,它可能有一个接口)我们没有java.net.URL类本身的实例。我认为这个问题可能会更明确一些。当然,它可能会更明确一些,但它是否是自定义URL有点含糊不清,抱歉-但它仍然说它是最终的,所以…有些东西可能是最终的,仍然实现了一个接口,将自己借给工厂或者注射施工。我把这个加上1,它不应该是-1,因为你可以创建一个工厂来做和你一样的事情