发生这种情况时,您有三种选择:
- 忽略该问题,不编写任何测试。
- 疯狂地进行重构,更改所有内容以使用依赖项注入。
- 使用PowerMock模拟构造函数
显然,选项1并不是一个严肃的选项,尽管我建议重构以将所有内容移至依赖项注入,但这需要时间,而且您必须务实。 这就是PowerMock的用处。此博客演示了如何使用PowerMock模拟构造函数,这意味着当您的代码调用new时,它不会创建真实的对象,而是会创建模拟对象。
为了证明这个想法,我们首先需要测试一些类,如下所示。
public class AnyOldClass {public String someMethod() {return "someMethod";}}
public class UsesNewToInstantiateClass {public String createThing() {AnyOldClass myclass = new AnyOldClass();String returnValue = myclass.someMethod();return returnValue;}}
第一个类AnyOldClass是代码通过调用new实例化的类。 在这个例子中,顾名思义,它可以是任何东西。
第二个类,恰当地命名为UsesNewToInstantiateClass,具有一个方法createThing(),该方法在调用时会执行以下操作:
AnyOldClass myclass = new AnyOldClass();
这一切都非常简单,因此我们将快速进行PowerMock辅助的JUnit测试:
import static org.easymock.EasyMock.expect;
import static org.junit.Assert.assertEquals;
import static org.powermock.api.easymock.PowerMock.expectNew;
import static org.powermock.api.easymock.PowerMock.replay;
import static org.powermock.api.easymock.PowerMock.verify;import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.annotation.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;@RunWith(PowerMockRunner.class)
@PrepareForTest(UsesNewToInstantiateClass.class)
public class MockConstructorTest {@Mockprivate AnyOldClass anyClass;private UsesNewToInstantiateClass instance;@Testpublic final void testMockConstructor() throws Exception {instance = new UsesNewToInstantiateClass();expectNew(AnyOldClass.class).andReturn(anyClass);final String expected = "MY_OTHER_RESULT";expect(anyClass.someMethod()).andReturn(expected);replay(AnyOldClass.class, anyClass);String result = instance.createThing();verify(AnyOldClass.class, anyClass);assertEquals(expected, result);}}
首先,该类具有通常的PowerMock附加功能:
@RunWith(PowerMockRunner.class)
@PrepareForTest(UsesNewToInstantiateClass.class)
在文件的顶部加上anyOldClass模拟对象的创建。 要考虑的重要代码行是:
expectNew(AnyOldClass.class).andReturn(anyClass);
这行代码告诉PowerMock期望调用新的AnyOldClass()并返回我们的anyClass模拟对象。
同样有趣的是重播和验证的调用。 在上面的示例中,它们都有两个参数。 第一个,AnyOldClass.class与上面的ExpectNew(…)调用有关,而第二个,anyClass则与简单的模拟调用Expect(anyClass.someMethod())。Return(expected);相关。
在某些时候,您确实应该让new来做它所做的事情:创建一个请求类型的新对象。 有一种观点认为,在测试时您可能过度隔离代码,而对所有内容进行模拟会降低测试的含义和价值。 对我来说,没有正确的答案,这是一个选择的问题。
很明显,如果您的代码访问诸如数据库之类的外部资源,那么您将重构并实现DI或使用PowerMock。 如果您的被测试代码不访问任何外部资源,那么更多的是判断多少代码隔离过多? 这也许需要一些思考,并且可能是另一天另一个博客的主题……
参考: 使用PowerMock从我们的JCG合作伙伴 Roger Hughes 嘲笑 “ Captain Debug's”博客中的 构造方法 。
翻译自: https://www.javacodegeeks.com/2012/09/using-powermock-to-mock-constructors.html