PowerMock使用expectNew创建UserPersistor时生成真实对象而非Mock对象问题
业务代码
public class PersistenceManager { public boolean addUser(User user) { UserPersistor userPersistor = new UserPersistor(user) { @Override void somemethod() { // TODO Auto-generated method stub } }; userPersistor.addUser(); System.out.println("PersistenceManager added user "); return true; } class User { public String firstName; public String lastName; public User(String firstName, String lastName) { super(); this.firstName = firstName; this.lastName = lastName; } } abstract class UserPersistor { public UserPersistor( ) { } public UserPersistor(User user) { } public void addUser() { System.err.println("UserPersistor added user "); } abstract void somemethod(); } }
单元测试代码
import static org.powermock.api.easymock.PowerMock.createMock; import static org.powermock.api.easymock.PowerMock.expectNew; import static org.powermock.api.easymock.PowerMock.expectLastCall; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.easymock.PowerMock; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @RunWith(PowerMockRunner.class) @PrepareForTest( PersistenceManager.class ) public class PersistenceManagerTest { private User user = null; @Before public void before() throws Exception { user = createMock(User.class); UserPersistor userPersistor = createMock(UserPersistor.class); userPersistor.addUser(); expectLastCall(); expectNew(UserPersistor.class, user).andReturn(userPersistor); PowerMock.replayAll(); } @Test public void testaddUser() { PersistenceManager tested = new PersistenceManager(); tested.addUser(user); PowerMock.verifyAll(); } }
问题根因
- 业务代码中实例化的是继承
UserPersistor的匿名内部类,而非UserPersistor本身。expectNew(UserPersistor.class, user)只会拦截直接实例化UserPersistor类的操作,无法匹配匿名子类的实例化逻辑,导致拦截失效,真实对象被创建。 UserPersistor是PersistenceManager的非静态内部类,它的所有构造器都会隐式接收外部类PersistenceManager的实例作为第一个入参,你写expectNew时只传入了user一个参数,和实际构造器的参数列表不匹配,进一步导致拦截规则无法命中。- 匿名内部类不在
@PrepareForTest的处理范围内,PowerMock无法修改其字节码完成实例替换。
修复建议
- 优先调整业务代码,移除
addUser方法中的匿名内部类写法,提前将UserPersistor的子类实现抽取为独立的普通类,保证expectNew可以匹配到目标类。 - 如果要保留非静态内部类结构,
expectNew需要补全隐式的外部类参数,同时调整测试代码中PersistenceManager实例的创建时机,示例调整如下:
@Before public void before() throws Exception { PersistenceManager tested = new PersistenceManager(); user = createMock(User.class); UserPersistor userPersistor = createMock(UserPersistor.class); userPersistor.addUser(); expectLastCall(); // 补全外部类实例作为第一个构造参数 expectNew(UserPersistor.class, tested, user).andReturn(userPersistor); PowerMock.replayAll(); }
- 不建议保留匿名内部类的写法,该写法会导致测试逻辑复杂度大幅提升,可维护性差。
内容的提问来源于stack exchange,提问作者Pankaj Kumar
相关产品推荐
相关产品推荐

