在 Java 中模拟 URL

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/565535/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 16:18:07  来源:igfitidea点击:

Mocking a URL in Java

javaurlmocking

提问by bowsie

We have a URL object in one of our Java classes that we want to mock, but it's a final class so we cannot. We do not want to go a level above, and mock the InputStream because that will still leave us with untested code (we have draconian test coverage standards).

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

I've tried jMockIt's reflective powers but we work on Macs and there are problems with the Java agent handler that I haven't been able to resolve.

我已经尝试过 jMockIt 的反射能力,但我们在 Mac 上工作,并且 Java 代理处理程序存在我无法解决的问题。

So are there any solutions that do not involve using real URLs in the junit test?

那么有没有不涉及在junit测试中使用真实URL的解决方案呢?

采纳答案by tvanfosson

When I have a class that can't be easily mocked because it is final (or sealed in C#), my usual route is to write a wrapper around the class and use the wrapper wherever I would use the actual class. Then I would mock out the wrapper class as necessary.

当我有一个不能轻易模拟的类时,因为它是最终的(或在 C# 中密封),我通常的方法是在类周围编写一个包装器,并在我将使用实际类的任何地方使用该包装器。然后我会根据需要模拟包装类。

回答by Rob Di Marco

I have used a URLHandler that allows me to load a URL from the classpath. So the following

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

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

would open a file named foo from within the class path. To do this, I use a common utility libraryand register a handler. To use this handler, you just need to call:

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

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

and the resource URL is now available.

并且资源 URL 现在可用。

回答by Andrzej Doyle

I would look again at why you wantto mock a final data object. Since by definition you aren't subclassing the object in your actual code, and it's not going to be the object under test, you shouldn't need to white-box test this code; just pass in whatever (real) URL objects are appropriate, and check the output.

我会再看看你为什么模拟最终的数据对象。由于根据定义,您不会在实际代码中对对象进行子类化,并且它不会是被测对象,因此您不需要对此代码进行白盒测试;只需传入任何合适的(真实)URL 对象,然后检查输出。

Mock objects are useful when it's difficult to create a real object appropriate, or the real object's method are either time-consuming or depend on some stateful external resource (like a database). Neither of these apply in this case so I can't see why you can't just construct a real URL object representing the appropriate resource location.

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

回答by Joel

Does the URL class implement an interface? If so then you could instantiate it using inversion of control or a configurable factory, rather than by direct construction, this would allow you to inject/construct a test instance at test runtime rather than the final instance you currently have.

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

回答by Rogério

JMockit does indeed allow you to mock a final JRE class like java.net.URL.

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

It seems the Attach API in jdkDir/lib/tools.jar available in implementations of JDK 1.6 other than Sun's does not work as well. I guess this stuff is still too new/advanced, or simply didn't get the necessary attention from the other JDK vendors (Apple, IBM with the J9 JDK, Oracle with the JRockit JDK).

似乎 jdkDir/lib/tools.jar 中的 Attach API 在 JDK 1.6 的实现中可用,而不是 Sun 的。我想这些东西仍然太新/太先进,或者根本没有得到其他 JDK 供应商(Apple、IBM 使用 J9 JDK、Oracle 使用 JRockit JDK)的必要关注。

So, if you run into problems by having tools.jar in the classpath, try using the "-javaagent:jmockit.jar" JVM argument. It tells the JVM to directly load the java agent at startup, without using the Attach API. That should work in the Apple JDK 1.5/1.6.

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

回答by Artefacto

Like Rob said, if what you want is to mock the connection returned from the URL, you can extend URLStreamHandler. For instance, with mockito:

就像 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");

回答by koljaTM

I went with the following:

我采用了以下方法:

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

This gives me a real URL object that contains my mock data.

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

回答by Richards Peter.

I think you can use Powermock to do this. I was able to mock URL class using PowerMock lately. Hope this helps.

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

/* Actual 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;
    }
}

/* Test class */

/* 测试类 */

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

回答by api

If you don't want to create a wrapper :

如果您不想创建包装器:

Register a URLStreamHandlerFactory

注册一个 URLStreamHandlerFactory

Make the method you want public

公开你想要的方法

Mock the chain

模拟链

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 : I don't know how to cancel the numbered list auto-spacing after last line

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

回答by Tobb

Create a URL-object pointing to the test class itself.

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

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