java - Powermockito 未在 URI.toURL() 方法中模拟 URL 构造函数

标签 java junit mockito powermock powermockito

此单元测试失败。

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

最佳答案

来自FAQ :

Question:

I cannot mock classes in from java.lang, java.net, java.io or other system classes, why?

Answer:

This is because they're loaded by Java's bootstrap classloader and cannot be byte-code manipulated by PowerMock's classloader. Since PowerMock 1.2.5 there's a work-around, please have a look at this simple example to see how it's done.

请注意,示例的链接已失效,应该是 this一个代替。

<小时/>

user674669 写道:

I am intercepting the constructor of URL class which happens in URI class.

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

user674669 写道:

So, I am preparing URI using PrepareForTest annotation.

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 对象。

关于java - Powermockito 未在 URI.toURL() 方法中模拟 URL 构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61239329/

相关文章:

unit-testing - 如何在蛋糕模式中使用模拟

java - volatile关于可见性及时性的详细语义

java - 从生成的图像中删除 batik 光栅化 Logo 水印

java - 简单的 Java 回调不起作用

java - 如何在 RxJava 服务器轮询中对 "repeatWhen"进行单元测试

java - 使用 InOrder 验证更改的 List 对象的方法调用

java - 如何从 Hibernate session 中分离所有对象

java - Junit Mockito 初始化错误

java - 正确放置线模的 Junit 测试类中的 stub

spring - 无法在 Hudson Maven 中为 JUnit 测试加载 ApplicationContext,但本地没有问题