如何使用Mockito Mock通过new关键字创建的对象(不使用PowerMock)
好问题!Mockito本身确实没办法直接Mock方法内部new出来的对象,但咱们完全可以通过重构代码提升可测试性的方式解决,根本不需要PowerMock。针对你给出的EventPublisher代码,我给你两种可行的方案:
方案1:依赖注入(最推荐,符合依赖倒置原则)
思路
把ObjectMapper从方法内部创建改为外部注入,这样测试时可以直接传入Mock的ObjectMapper实例,彻底避免在方法内部硬编码创建依赖的问题,同时让代码耦合度更低。
修改后的业务代码
public class EventPublisher { // 将ObjectMapper作为成员变量,通过构造方法注入 private final ObjectMapper objectMapper; // 提供构造方法,用于外部注入ObjectMapper public EventPublisher(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } public void publishEvent(final String eventJson) { try { // 直接使用注入的ObjectMapper,不再调用内部创建方法 // 注:原代码里的`event`变量看起来是笔误,这里假设你是要处理传入的eventJson Test testJson = objectMapper.readValue(eventJson, Test.class); testJson.getName(); } catch (JsonProcessingException e) { return; } // publish(); some external call } // 如果其他地方仍需要创建特定配置的ObjectMapper,可以保留此方法,基于注入的实例做调整 private ObjectMapper getObjectMapper(boolean someFlag) { return objectMapper.copy().enable(SerializationFeature.INDENT_OUTPUT); } }
测试代码
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) public class EventPublisherTest { @Mock private ObjectMapper mockObjectMapper; // Mock ObjectMapper实例 @InjectMocks private EventPublisher eventPublisher; // 自动将Mock的ObjectMapper注入到EventPublisher @Test public void testPublishEvent_WhenJsonProcessingFails_ReturnsEarly() throws JsonProcessingException { // 模拟JSON解析失败的场景 String testEventJson = "{invalid-json}"; doThrow(JsonProcessingException.class).when(mockObjectMapper) .readValue(testEventJson, Test.class); // 调用目标方法 eventPublisher.publishEvent(testEventJson); // 验证后续的外部调用(比如publish())没有被执行(如果需要的话) // verify(eventPublisher, never()).publish(); } }
方案2:重构创建方法为protected,用Mockito Spy替换返回值
思路
如果不想大幅修改现有代码结构,可以把创建ObjectMapper的getObjectMapper方法访问权限从private改为protected,这样测试时可以用Mockito的Spy来监视EventPublisher实例,并覆盖这个方法的返回值,返回我们Mock的ObjectMapper。
修改后的业务代码
public class EventPublisher { public void publishEvent(final String eventJson) { try { ObjectMapper objectMapper = getObjectMapper(true); // 同样修正笔误,假设处理传入的eventJson Test testJson = objectMapper.readValue(eventJson, Test.class); testJson.getName(); } catch (JsonProcessingException e) { return; } // publish(); some external call } // 将private改为protected,允许子类(包括Spy生成的动态子类)重写 protected ObjectMapper getObjectMapper(boolean someFlag) { ObjectMapper mapper = new ObjectMapper(); if (someFlag) { // 原有的配置逻辑 mapper.enable(SerializationFeature.INDENT_OUTPUT); } return mapper; } }
测试代码
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) public class EventPublisherTest { @Spy private EventPublisher eventPublisherSpy; // Spy真实的EventPublisher实例 @Test public void testPublishEvent_WithMockedObjectMapper() throws JsonProcessingException { // 创建Mock的ObjectMapper ObjectMapper mockMapper = mock(ObjectMapper.class); // 当调用getObjectMapper(true)时,返回我们的Mock实例 doReturn(mockMapper).when(eventPublisherSpy).getObjectMapper(true); // 模拟JSON解析后的Test对象行为 Test mockTestObj = mock(Test.class); when(mockMapper.readValue(anyString(), eq(Test.class))).thenReturn(mockTestObj); // 调用目标方法 eventPublisherSpy.publishEvent("valid-event-json"); // 验证Test对象的getName()方法被调用 verify(mockTestObj).getName(); } }
额外说明
- 方案1是更优的选择,它不仅解决了测试问题,还让代码更符合面向对象设计原则,后续维护和扩展也更方便。
- 原代码里的
event变量看起来是笔误,我在示例中假设你要处理的是传入的eventJson,你可以根据实际业务调整。
内容的提问来源于stack exchange,提问作者Kiran
相关产品推荐
相关产品推荐

