[英]In Java with PowerMocktio, how do you mock the constructor of a class when it's instantiated inside a private nested class?
我有一个包含要测试的几种方法的类,以及在其中一些方法中使用的私有嵌套类。 在这个私有的嵌套类中,它创建一个对象,该对象试图与网站或数据库建立连接。 我想分开测试,以连接到该外部资源以及对检索到的信息进行的处理。 这样,我们可以选择不将测试环境连接到功能“外部”源,从而大大简化了这些测试所需的设置。
为此,我正在编写一个测试,该测试模拟试图建立这些连接的对象的构造函数。 当嵌套的私有类尝试形成连接时,并且当它尝试检索信息时,我不希望它做任何事情,我希望它仅返回预定义的数据字符串。 目前,我有一些类似的东西:
public class MyClass {
public int mainMethod() {
//Some instructions...
NestedClass nestedClass = new NestedClass();
int finalResult = nestedClass.collectAndRefineData();
}
private class NestedClass {
public NestedClass() {
Connector connect = new Connector();
}
public int collectAndRefineData() {
//Connects to the outside resource, like a website or database
//Processes and refines data into a state I want
//Returns data
}
}
测试类如下所示:
@RunWith(PowerMockRunner.class)
@PrepareForTest({Connector.class})
public class MyClassTests {
@Test
public void testOne() {
mockConnector = mock(Connection.class);
PowerMockito.whenNew(Connector.class).withNoArguments().thenReturn(mockConnector);
MyClass testClass = new MyClass();
int result = testClass.mainMethod();
Assert.equals(result, 1);
}
}
现在,我确实知道 ,在PrepareForTest批注中,我需要包括实例化要为其模拟构造函数的对象的类。 问题是我不能放MyClass,因为那不是创建它的对象,我不能放NestedClass,因为它在测试中看不到。 我曾尝试将MyClass.class.getDeclaredClasses [1]放入正确的类中,但是不幸的是PowerMocktio需要在注释中添加一个常量,这根本行不通。
谁能想到一种使此模拟构造函数起作用的方法?
注意:我无法对正在测试的代码进行任何更改。 这是因为该代码目前可以正常工作,已经过手动测试,我正在编写此代码,以便将来的项目将使用此自动测试框架。
我不确定您是否正在运行与Mockito集成的测试,如果可以,则可以使用以下代码:
@RunWith(PowerMockRunner.class)
@PrepareForTest({Connector.class})
public class MyClassTests {
@Mock
Connector mockConnector;
@InjectMocks
MyClass testClass;
@Test
public void testOne() {
PowerMockito.whenNew(Connector.class).withNoArguments().thenReturn(mockConnector);
int result = testClass.mainMethod();
Assert.equals(result, 1);
}
}
我怀疑您将不得不修改正在测试的代码。 您有两种选择:
最好同时做两个。 有关更多详细信息,请参见《 有效处理旧版代码 》一书。
我将显示一个选项2的示例。
首先,您可以创建一个“ seam”,以使测试能够更改Connector
的创建方式:
public class MyClass {
public int mainMethod() {
// Some instructions...
NestedClass nestedClass = new NestedClass();
return nestedClass.CollectAndRefineData();
}
// visible for testing
Connector createConnector() {
return new Connector();
}
private class NestedClass {
private final Connector connector;
public NestedClass() {
connector = createConnector();
}
...
}
}
然后,您可以使用MyClass
的部分模拟来测试您的代码。
@RunWith(JUnit4.class)
public class MyClassTests {
@Test
public void testOne() {
MyClass testClass = spy(MyClass.class);
Connector mockConnector = mock(Connector.class);
when(testClass.createConnection())
.thenReturn(mockConnector);
int result = testClass.mainMethod();
Assert.assertEquals(1, result);
}
}
请注意, assertEquals
期望第一个参数为期望值。
另请注意,此测试使用Mockito而不是PowerMock 。 这样做很好,因为使用PowerMock的测试可能会很脆弱,并且由于被测代码的微小更改而容易损坏。 请注意,使用部分模拟也可能很脆弱。 我们会尽快解决。
通过测试后,您可以重构代码,以便调用方通过Connector
工厂:
public class MyClass {
private final ConnectorFactory connectorFactory;
@Inject
MyClass(ConnectorFactory factory) {
this.connectorFactory = factory;
}
// visible for testing
Connector createConnector() {
return connectorFactory.create();
}
private class NestedClass {
private final Connector connector;
public NestedClass() {
connector = createConnector();
}
...
}
}
使用MyClass
的代码最好使用依赖注入框架,例如Guice或Spring。 如果这不是一个选择,则可以使第二个无参数构造函数传入真实的ConnectorFactory
。
假设测试仍然通过,则可以通过将测试更改为模拟ConnectorFactory
而不是对MyClass
进行部分模拟来使测试不那么脆弱 。 当这些测试通过时,可以内联createConnector()
。
将来,请尝试在编写代码时编写测试 (或至少在花费大量时间进行手动测试之前)。
mockConnector =模拟(Connection.class);
未声明此对象。 无论如何,无论如何,您都必须使用@Mock注释。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.