【问题标题】:Powermockito not mocking URL constructor in URI.toURL() methodPowermockito 不在 URI.toURL() 方法中模拟 URL 构造函数
【发布时间】:2020-04-15 21:57:24
【问题描述】:

此单元测试失败。

package com.abc;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import java.net.URI;
import java.net.URL;

import static org.junit.Assert.assertEquals;

@RunWith(PowerMockRunner.class)
@PowerMockIgnore("javax.management.*")
@PrepareForTest({URI.class})
public class ITest2 {
    @Test
    public void test5() throws Exception {
        URI uri = new URI("http://www.google.com");

        final URL resourceUrl = ClassLoader.getSystemClassLoader().getResource("static/abc.png"); //EXISTS

        PowerMockito.whenNew(URL.class).withArguments(
                "http://www.google.com")
                .thenReturn(resourceUrl);

        URL url = uri.toURL(); // <--- At this point, url should be == resourceUrl

        assertEquals(resourceUrl, url); // <--- url is http://www.google.com and not ".../static/abc.png"
    }
}

此单元测试失败。

java.lang.AssertionError: 
Expected :file:/Users/hidden/target/classes/static/abc.png
Actual   :http://www.google.com
<Click to see difference>

你知道为什么 url != resourceUrl 吗?我错过了什么?

URI.toURL() 的代码如下:

public URL toURL()
    throws MalformedURLException {
    if (!isAbsolute())
        throw new IllegalArgumentException("URI is not absolute");
    return new URL(toString());
}

使用 Mockito 2.15 和 Powermock 2.0.7。

谢谢。

更新:

添加这些也无济于事。只是偷偷摸摸。

PowerMockito.whenNew(URL.class).withArguments(
        eq("http://www.google.com"))
        .thenReturn(resourceUrl);

PowerMockito.whenNew(URL.class).withArguments(Mockito.anyString()).thenReturn(resourceUrl);
PowerMockito.whenNew(URL.class).withArguments(any()).thenReturn(resourceUrl);
PowerMockito.whenNew("java.net.URL").withArguments(any()).thenReturn(resourceUrl);

PowerMockito.whenNew(URL.class).withParameterTypes(String.class)
        .withArguments("http://www.google.com")
        .thenReturn(resourceUrl);

PowerMockito.whenNew(URL.class).withAnyArguments().thenReturn(resourceUrl);

PowerMockito.whenNew(URL.class).withNoArguments().thenReturn(resourceUrl);

【问题讨论】:

  • @kasptom,不,它没有回答我的问题。你知道我做错了什么吗?谢谢。
  • PowerMockito 在模拟系统类方面有一定的限制,请查看here。但是,这种方法对您来说是不可能的,因为 java.net.URL 是另一个系统类。
  • 您可以添加一个测试,以显示您真正尝试做的更多上下文。 Mabye 嘲笑 java.net.URI 可以解决您的问题。只要可以修改创建类的字节码,这至少应该是可能的。
  • @second,我在上面的代码示例中有一个名为 test5() 的简单测试。

标签: java junit mockito powermock powermockito


【解决方案1】:

来自FAQ

问题:

我无法从 java.lang、java.net、java.io 或其他系统类中模拟类,为什么?

答案:

这是因为它们是由 Java 的引导类加载器加载的,并且 PowerMock 的类加载器不能处理字节码。自从 PowerMock 1.2.5 有一个解决方法,请看看这个 简单的例子来看看它是如何完成的。

请注意,该示例的链接是一个死链接,应该是 this 一个。


user674669 写道:

我正在拦截 URI 类中发生的 URL 类的构造函数。

您正在尝试这样做。然而,这默默地失败了(而是创建了真实的对象)。

user674669 写道:

所以,我正在使用 PrepareForTest 注释准备 URI。

java.net.URI 是一个系统类。如上所述,PowerMockito 无法为其操作字节码,因此不会产生任何影响。您将不得不调整不同类的字节码,这意味着您需要在 @PrepareForTest 注释中定义不同的类。


linked article 中提到的案例的工作示例:

powermock 2.0.7mockito 3.3.3junit 4测试

public class ClassUnderTest {
    public InputStream method(boolean param, URI uri) throws Exception {
        String scheme = param ? "https" : "http";
        URI replacedUri = new URI(scheme, uri.getAuthority(), uri.getPath(), uri.getQuery(), uri.getFragment());
        return replacedUri.toURL().openStream();
    }
}
@RunWith(PowerMockRunner.class)
@PrepareForTest(ClassUnderTest.class)
public class MyTest { 

    @Test
    public void testMethod() throws Exception { 

        URI uri = new URI("blah://localhost");
        FileInputStream fis = new FileInputStream(new File(".", "existing.file"));
        
        URI uriMock = PowerMockito.mock(URI.class); 
        URL urlMock = PowerMockito.mock(URL.class); 
        
        PowerMockito.whenNew(URI.class).withAnyArguments().thenReturn(uriMock); 
        Mockito.when(uriMock.toURL()).thenReturn(urlMock); 
        Mockito.when(urlMock.openStream()).thenReturn(fis); 
        
        ClassUnderTest testObject = new ClassUnderTest(); 
        InputStream is = testObject.method(false, uri);

        Assert.assertEquals(fis, is);
    }
}

这是有效的,因为 ClassUnderTest 的字节码已修改 并且定义的模拟被放置在那里,而不是创建一个真正的URI 对象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-28
    • 2017-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-24
    相关资源
    最近更新 更多