如何用Mockito和JUnit模拟链式依赖调用的方法行为?
我明白你现在卡在链式依赖的Mock上了——这种嵌套调用确实容易踩坑,尤其是单独Mock每个类却没把它们串联起来的时候。咱们一步步来解决这个问题:
核心问题分析
你遇到的问题本质是:没有把Mock对象逐层串联成调用链。如果只给HappyPost、User、Images加了@Mock注解,但没告诉Mockito「调用post.getUser()要返回你Mock的User对象」、「调用user.getImage()要返回你Mock的Images对象」,那链式调用到中间就会返回null,自然没法拿到最终的imageUrl。
解决方案步骤
1. 先确保Mock对象正确初始化
如果用JUnit 5,推荐用@ExtendWith(MockitoExtension.class)自动初始化所有@Mock注解的对象;如果是JUnit 4,要在测试方法/@Before方法里手动调用MockitoAnnotations.openMocks(this),不然@Mock对象都是null,直接抛空指针。
2. 逐层串联Mock的返回关系
咱们把调用链拆解开,给每一层设置对应的返回Mock对象,最后给最底层的方法指定返回值。
完整代码示例
先简化你的实体类结构(假设是这样):
// 你的实体类 public class HappyPost { private User user; public User getUser() { return user; } } public class User { private Images image; public Images getImage() { return image; } } public class Images { private String imageUrl; public String getImageUrl() { return imageUrl; } } public class HappyPostsDto { private String image; public void setImage(String image) { this.image = image; } public String getImage() { return image; } // 方便验证结果 }
然后是测试类的正确写法:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import static org.mockito.Mockito.when; import static org.junit.jupiter.api.Assertions.assertEquals; @ExtendWith(MockitoExtension.class) // JUnit5自动初始化Mock对象 public class HappyPostChainTest { @Mock private HappyPost happyPost; @Mock private User user; @Mock private Images images; @Test public void testSetImageFromChainCall() { // 1. 逐层设置链式调用的返回值 when(happyPost.getUser()).thenReturn(user); // post.getUser()返回Mock的User when(user.getImage()).thenReturn(images); // user.getImage()返回Mock的Images when(images.getImageUrl()).thenReturn("https://my-site.com/user-avatar.png"); // 指定图片URL // 2. 执行你的业务代码 HappyPostsDto happyPostsDto = new HappyPostsDto(); happyPostsDto.setImage(happyPost.getUser().getImage().getImageUrl()); // 3. 验证结果(可选,确保逻辑正确) assertEquals("https://my-site.com/user-avatar.png", happyPostsDto.getImage()); } }
可选:更简洁的深度Mock写法
如果你的链式调用比较简单,也可以用Mockito的深度Mock特性,一次性创建支持嵌套调用的Mock对象,省去逐层设置的步骤:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Answers; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import static org.mockito.Mockito.when; import static org.junit.jupiter.api.Assertions.assertEquals; @ExtendWith(MockitoExtension.class) public class HappyPostDeepMockTest { // 创建支持深度Mock的HappyPost对象 @Mock(answer = Answers.RETURNS_DEEP_STUBS) private HappyPost happyPost; @Test public void testDeepMockChainCall() { // 直接给整个链式调用设置返回值 when(happyPost.getUser().getImage().getImageUrl()).thenReturn("https://my-site.com/user-avatar.png"); HappyPostsDto happyPostsDto = new HappyPostsDto(); happyPostsDto.setImage(happyPost.getUser().getImage().getImageUrl()); assertEquals("https://my-site.com/user-avatar.png", happyPostsDto.getImage()); } }
不过要注意:深度Mock虽然方便,但如果链式调用逻辑复杂,排查问题会比较麻烦,所以更推荐第一种逐层设置的方式,可读性和可控性更强。
常见错误排查
- 忘记初始化Mock:没加
@ExtendWith(MockitoExtension.class)或调用MockitoAnnotations.openMocks(this),导致@Mock对象为null; - 返回真实对象而非Mock:比如你之前写了
when(happyPost.getUser()).thenReturn(new User()),但new出来的User对象的getImage()返回null,链式调用直接中断; - 遗漏某一层的返回设置:比如只设置了
post.getUser()和image.getImageUrl(),但没设置user.getImage(),导致调用getImageUrl()时抛空指针。
内容的提问来源于stack exchange,提问作者shovon
相关产品推荐
相关产品推荐

