无法使用PowerMockito/Mockito模拟URL类

问题描述:

我想使用PowerMockito来模拟我测试的代码中的java.net.URL类的创建。基本上,我想阻止发生真正的HTTP请求,而不是1)在请求发生时检查数据,2)在模拟的响应中提供自己的测试数据。这就是我想:无法使用PowerMockito/Mockito模拟URL类

@RunWith(PowerMockRunner.class) 
@PrepareForTest({ URL.class, MockedHttpConnection.class }) 
public class Test { 
    URL mockedURL = PowerMockito.mock(URL.class); 
    MockedHttpConnection mockedConnection = PowerMockito.mock(MockedHttpConnection.class); 

... 
    PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL); 
PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection); 

... 
} 

,我想测试看起来像这样的代码:

URL wlInvokeUrl = new URL(wlInvokeUrlString); 
connection = (HttpURLConnection) wlInvokeUrl.openConnection(); 

在我的测试场景之前我嘲笑wlInvokeUrlString匹配“MyURLString”。我也尝试过使用whenNew行的各种其他形式,试图注入模拟。无论我尝试什么,它都不会拦截构造函数。我想要做的就是“捕捉”openConnection()的调用,并让它返回我的模拟HTTP连接而不是真正的连接。

我在同一个脚本中嘲笑了这个之前的其他类,并且这些工作正常。要么我需要第二双眼睛(可能是真的),或者URL类有一些独特的东西。我注意到,如果我使用“whenNew(URL.class).withAnyArguments()”并将“thenReturn”更改为“thenAnswer”,我可以触发它。唯一的问题是我从来没有得到我的代码的URL调用。我看到的是URL.class的三参数构造函数的调用,所有参数都为空。这个类是否来自Java运行时并且由测试运行器引导?任何帮助深表感谢。

+0

你不需要Powermock来测试这个。只是模拟字符串url,例如使用基于文件的路径(“file://my-test.properties”)。这将起作用,并且你可以传递你需要的数据 –

+0

@JérémieB你能详细说明一下吗?这看起来像是一个优雅而简单的解决方案,但被测试的代码需要HttpURLConnection,如果我将URL字符串更改为引用文件(即java.lang.ClassCastException:sun.net.www.protocol。),我会得到一个ClassCastException。 ftp.FtpURLConnection不能转换为java.net.HttpURLConnection)。另外,如何使用这种方法验证请求上的输入参数? – Jim

+0

您不应该直接使用'HttpURLConnection',而是使用'URL'和'URLConnection'。 'HttpURLConnection'没有什么用处。这是URL抽象传输层的目的。 –

我不确定.withParameterTypes(x).withArguments(x)之间的区别,但我相信您需要按照以下步骤对代码进行设置。尝试一下,让我知道如果这没有帮助。

PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection); 
PowerMockito.whenNew(URL.class).withArguments(Mockito.anyString()).thenReturn(mockedURL); 
+0

我试过这些行,但是我看到了相同的结果。不使用模拟对象,而是创建并使用真正的URL和HttpURLConnection类。问题可能是因为URL类被声明为final吗?从我读过的PowerMockito应该能够处理这个问题。 – Jim

问题是真正的调用的参数与您的模拟中预期的不匹配。

PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL)将返回mockedURL只有呼叫是new URL("MyURLString")

如果将其更改为:

PowerMockito.whenNew(URL.class).withParameterTypes(String.class) 
    .withArguments(org.mockito.Matchers.any(String.class)).thenReturn(mockedURL); 

它可以捉住传递给构造URL(String)(甚至null)任何字符串并返回你的模拟。


当你试图

“whenNew(URL.class).withAnyArguments()” 和改变 “thenReturn” 到 “thenAnswer” 我能得到它的触发。唯一的问题是我从来没有得到我的代码的URL调用 。我看到的是针对URL.class的 三参数构造函数的调用,其中 参数的所有空值。

PowerMock会尽量嘲笑所有构造函数(org.powermock.api.mockito.internal.expectation.DelegatingToConstructorsOngoingStubbing.InvokeStubMethod在线路122),那么它调用第一个(3个参数)和讥笑它的答案。但随后的电话将返回已经嘲弄的电话,因为您告诉它嘲笑任何参数。这就是为什么您在Answer中只看到null, null, null的一个电话。

+0

我在尝试做这项工作的过程中尝试了很多事情,包括对您的建议的变体。我通过更改whenNew这一行来重新测试,但仍得到相同的结果。任何其他想法? – Jim

这是使用PowerMockito.whenNew时常见的错误。

请注意,您必须准备创建用于测试的MyClass的新实例的类,而不是MyClass本身。例如。如果该类做新MyClass的()被称为X,那么你不得不为了做@PrepareForTest(X.class)为whenNew工作

Powermock wiki

所以,你需要一点点改变您的测试,并加入到@PrepareForTest其创建的URL一个新实例类,如:

@RunWith(PowerMockRunner.class) 
@PrepareForTest({ URL.class, MockedHttpConnection.class , ConnectionUser.class}) 
public class URLTest { 
public class URLTest { 

    private ConnectionUser connectionUser; 

    @Before 
    public void setUp() throws Exception { 

     connectionUser = new ConnectionUser(); 
    } 

    @Test 
    public void testName() throws Exception { 

     URL mockedURL = PowerMockito.mock(URL.class); 
     MockedHttpConnection mockedConnection = PowerMockito.mock(MockedHttpConnection.class); 

     PowerMockito.whenNew(URL.class).withParameterTypes(String.class).withArguments("MyURLString").thenReturn(mockedURL); 
     PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection); 

     connectionUser.open(); 

     assertEquals(mockedConnection, connectionUser.getConnection()); 


    } 
} 

其中:

public class ConnectionUser { 

    private String wlInvokeUrlString = "MyURLString"; 
    private HttpURLConnection connection; 

    public void open() throws IOException { 
     URL wlInvokeUrl = new URL(wlInvokeUrlString); 
     connection = (HttpURLConnection) wlInvokeUrl.openConnection(); 
    } 

    public HttpURLConnection getConnection() { 
     return connection; 
    } 
} 
+0

你的答案最接近工作。进行这些更改后,将调用模拟URL构造函数。但是,它总是返回一个空对象。通过这个,我的意思是'URL wlInvokeUrl = new URL(wlInvokeUrlString);'总是将wlInvokeUrl赋值为null。我已经通过重构我的代码解决了原始问题,但是如果您有任何其他建议,我仍然致力于看到PowerMockito为此工作。 – Jim

+0

你使用什么版本的PowerMock,Mockito,Java?我使用最新版本和Mockito 1.10.19进行了测试,只有在我替换'mockedUrl'via'null'时才能重现您的问题。像这样:'PowerMockito.whenNew(URL.class).withArguments(“MyURLString”)。然后返回(null);'在所有其他情况下,我的版本的测试通过。顺便说一下,我使用Java 8来运行测试。 –

+0

你可以尝试在你的环境中运行我的版本吗? –