使用PowerMock模拟构造函数

本文介绍如何在Java中使用PowerMock模拟构造函数,以便在测试中避免直接实例化真实对象,特别是当代码依赖于new操作时。通过示例展示了如何设置预期的新实例化行为,并讨论了过度模拟可能带来的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我认为,依赖项注入的主要好处之一是可以将模拟和/或存根对象注入代码中,以提高可测试性,增加测试覆盖率并编写更好,更有意义的测试。 但是,有时候您会遇到一些不使用依赖注入的传统代码,而是通过组合而不是聚合将它们结合在一起。

发生这种情况时,您有三种选择:

  1. 忽略该问题,不编写任何测试。
  2. 疯狂地进行重构,更改所有内容以使用依赖项注入。
  3. 使用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 {

  @Mock
  private AnyOldClass anyClass;

  private UsesNewToInstantiateClass instance;

  @Test
  public 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值