无法使用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运行时并且由测试运行器引导?任何帮助深表感谢。
我不确定.withParameterTypes(x)
和.withArguments(x)
之间的区别,但我相信您需要按照以下步骤对代码进行设置。尝试一下,让我知道如果这没有帮助。
PowerMockito.when(mockedURL.openConnection()).thenReturn(mockedConnection);
PowerMockito.whenNew(URL.class).withArguments(Mockito.anyString()).thenReturn(mockedURL);
我试过这些行,但是我看到了相同的结果。不使用模拟对象,而是创建并使用真正的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
的一个电话。
我在尝试做这项工作的过程中尝试了很多事情,包括对您的建议的变体。我通过更改whenNew这一行来重新测试,但仍得到相同的结果。任何其他想法? – Jim
这是使用PowerMockito.whenNew
时常见的错误。
请注意,您必须准备创建用于测试的MyClass的新实例的类,而不是MyClass本身。例如。如果该类做新MyClass的()被称为X,那么你不得不为了做@PrepareForTest(X.class)为whenNew工作
所以,你需要一点点改变您的测试,并加入到@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;
}
}
你的答案最接近工作。进行这些更改后,将调用模拟URL构造函数。但是,它总是返回一个空对象。通过这个,我的意思是'URL wlInvokeUrl = new URL(wlInvokeUrlString);'总是将wlInvokeUrl赋值为null。我已经通过重构我的代码解决了原始问题,但是如果您有任何其他建议,我仍然致力于看到PowerMockito为此工作。 – Jim
你使用什么版本的PowerMock,Mockito,Java?我使用最新版本和Mockito 1.10.19进行了测试,只有在我替换'mockedUrl'via'null'时才能重现您的问题。像这样:'PowerMockito.whenNew(URL.class).withArguments(“MyURLString”)。然后返回(null);'在所有其他情况下,我的版本的测试通过。顺便说一下,我使用Java 8来运行测试。 –
你可以尝试在你的环境中运行我的版本吗? –
你不需要Powermock来测试这个。只是模拟字符串url,例如使用基于文件的路径(“file://my-test.properties”)。这将起作用,并且你可以传递你需要的数据 –
@JérémieB你能详细说明一下吗?这看起来像是一个优雅而简单的解决方案,但被测试的代码需要HttpURLConnection,如果我将URL字符串更改为引用文件(即java.lang.ClassCastException:sun.net.www.protocol。),我会得到一个ClassCastException。 ftp.FtpURLConnection不能转换为java.net.HttpURLConnection)。另外,如何使用这种方法验证请求上的输入参数? – Jim
您不应该直接使用'HttpURLConnection',而是使用'URL'和'URLConnection'。 'HttpURLConnection'没有什么用处。这是URL抽象传输层的目的。 –