如何使用Mockito模拟链式方法调用?含业务代码场景
Mockito模拟链式方法调用的分步指南(针对你的ViewModel创建函数)
嘿,我来帮你搞定这个Mockito模拟链式调用的问题!你的createViewModel函数里有一串很长的链式调用,要mock它其实不难,只需要逐层拆解每一步的对象,然后依次设置mock的返回值就行。下面是具体的步骤和代码示例:
第一步:识别所有需要Mock的对象
先把你的链式调用拆解开,看看每一步返回的是什么对象:product.getProductData().offers().get(0).productCategory().asProductCategory().inlined().map(...).map(...).orElse("")
拆解后需要mock的对象包括:
ClassB(也就是传入的product参数)ProductData(product.getProductData()的返回值)Offer(offers列表的第一个元素)productCategory()返回的包装类(假设叫WrappedProductCategory,你需要替换成实际类型)ProductCategory(asProductCategory()的返回值,以及inlined()里的对象)ProductCategory.GLProductGroup(glProductGroup()的返回值)
第二步:创建Mock对象
你可以用Mockito的@Mock注解或者Mockito.mock()方法来创建这些mock实例,比如在测试类里:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.List; import java.util.Optional; @ExtendWith(MockitoExtension.class) class YourViewModelCreatorTest { // 声明所有需要mock的对象(替换成你实际的类名) @Mock private ClassB mockProduct; @Mock private ProductData mockProductData; @Mock private Offer mockOffer; @Mock private WrappedProductCategory mockWrappedProductCategory; @Mock private ProductCategory mockInlinedProductCategory; @Mock private ProductCategory.GLProductGroup mockGLProductGroup; // 测试方法写在这里 }
第三步:逐层设置Mock的返回值
从最内层的对象开始设置返回,这样外层的链式调用才能正确拿到预期值:
@Test void createViewModel_ShouldSetName_FromGLProductGroup() throws ModuleException { // 1. 设置最内层:GLProductGroup的symbol返回预期字符串 when(mockGLProductGroup.symbol()).thenReturn("ELECTRONICS"); // 2. 设置inlined的ProductCategory返回这个GLProductGroup when(mockInlinedProductCategory.glProductGroup()).thenReturn(mockGLProductGroup); // 3. 设置asProductCategory()后的对象的inlined()返回包含mock对象的Optional when(mockWrappedProductCategory.asProductCategory().inlined()).thenReturn(Optional.of(mockInlinedProductCategory)); // 注意:如果asProductCategory()直接返回ProductCategory类型,那改成: // when(mockInlinedProductCategory.inlined()).thenReturn(Optional.of(mockInlinedProductCategory)); // 4. 设置Offer的productCategory()返回包装类对象 when(mockOffer.productCategory()).thenReturn(mockWrappedProductCategory); // 5. 设置ProductData的offers()返回包含mockOffer的列表 when(mockProductData.offers()).thenReturn(List.of(mockOffer)); // 6. 设置ClassB的getProductData()返回mockProductData when(mockProduct.getProductData()).thenReturn(mockProductData); // 调用目标方法(替换成你的实际类名) YourViewModelCreator creator = new YourViewModelCreator(); ClassA result = creator.createViewModel(mockProduct, null); // classCVar可以传null或者mock对象 // 验证结果:name应该是我们设置的"ELECTRONICS" assertEquals("ELECTRONICS", result.getName()); }
额外提示:处理null分支
如果要测试product为null或者product.getProductData()为null的情况,只需要调整mock的返回值即可:
@Test void createViewModel_ShouldSetEmptyName_WhenProductIsNull() throws ModuleException { YourViewModelCreator creator = new YourViewModelCreator(); ClassA result = creator.createViewModel(null, null); assertEquals("", result.getName()); } @Test void createViewModel_ShouldSetEmptyName_WhenProductDataIsNull() throws ModuleException { when(mockProduct.getProductData()).thenReturn(null); YourViewModelCreator creator = new YourViewModelCreator(); ClassA result = creator.createViewModel(mockProduct, null); assertEquals("", result.getName()); }
不推荐的方式:RETURNS_DEEP_STUBS
Mockito有个RETURNS_DEEP_STUBS选项可以一次性mock深层链式调用,但不建议用,因为它会让测试变得不透明,一旦链式调用有变化,测试很难排查问题。比如:
// 不推荐的写法,维护成本高 ClassB mockProduct = mock(ClassB.class, RETURNS_DEEP_STUBS); when(mockProduct.getProductData().offers().get(0).productCategory().asProductCategory().inlined() .map(ProductCategory::glProductGroup).map(ProductCategory.GLProductGroup::symbol).orElse("")) .thenReturn("ELECTRONICS");
这种写法看起来简洁,但出问题时很难定位,还是逐层mock更清晰可靠。
内容的提问来源于stack exchange,提问作者user3608245
相关产品推荐
相关产品推荐

