You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在单元测试中Mock iText 7.1.1以规避商业许可证使用的可行性问询

在单元测试中Mock iText 7.1.1以规避商业许可证使用的可行性问询

当然可行!而且用Mockito这类主流Mock工具的话,完全不用费太大力气就能搞定——核心就是把iText的所有依赖对象都Mock掉,让测试只走你自己的业务逻辑,根本不会触发商业许可证的校验。

我给你梳理下具体的实现思路和代码示例,都是实际项目里常用的方案:

1. 先搞定Mock工具的准备

首先确保你的测试依赖里有JUnit 5 + Mockito,如果是Maven项目,pom.xml里加这些依赖(版本选你项目兼容的就行):

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.9.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>4.11.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>4.11.0</version>
    <scope>test</scope>
</dependency>

另外,iText 7的很多核心类(比如PdfWriter)是final类,Mockito默认不能Mock final类,所以要开启它的inline Mock支持:在src/test/resources/mockito-extensions/目录下新建一个名为org.mockito.plugins.MockMaker的文件,里面只写一行:

mock-maker-inline

2. 测试代码的核心实现

你不需要大改业务代码,用Mockito的mockConstruction()(Mockito 3.5+支持)就能Mock iText类的构造函数,直接替换成Mock实例。下面是完整的测试示例:

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.MockedConstruction;
import org.mockito.junit.jupiter.MockitoExtension;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import java.io.ByteArrayOutputStream;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class PdfGenerationServiceTest {

    // 注入你的实际服务类
    @InjectMocks
    private PdfGenerationService yourPdfService;

    @Test
    void createPdf_ShouldExecuteAllBusinessLogic_WithMockedIText() throws Exception {
        // Mock PdfWriter的构造函数,返回Mock实例
        try (MockedConstruction<PdfWriter> mockedPdfWriter = mockConstruction(PdfWriter.class)) {
            // Mock PdfDocument的构造函数,同时设置close()啥也不做
            try (MockedConstruction<PdfDocument> mockedPdfDoc = mockConstruction(PdfDocument.class,
                    (mock, context) -> doNothing().when(mock).close())) {
                // Mock Document的构造函数,设置add()和close()的Mock行为
                try (MockedConstruction<Document> mockedDocument = mockConstruction(Document.class,
                        (mock, context) -> {
                            doNothing().when(mock).add(any());
                            doNothing().when(mock).close();
                        })) {
                    // Mock Paragraph的构造函数,链式方法要返回自身(因为iText的方法是fluent风格)
                    try (MockedConstruction<Paragraph> mockedParagraph = mockConstruction(Paragraph.class,
                            (mock, context) -> {
                                when(mock.setFont(any())).thenReturn(mock);
                                when(mock.setFontSize(anyFloat())).thenReturn(mock);
                                when(mock.setMarginTop(anyFloat())).thenReturn(mock);
                            })) {

                        // 准备测试数据
                        PdfData testPdfData = new PdfData();
                        testPdfData.setSomeStr("Test Content");

                        // 调用要测试的方法
                        byte[] result = yourPdfService.createPdf(testPdfData);

                        // 验证核心逻辑:比如Document的add方法被调用了一次
                        Document mockDoc = mockedDocument.constructed().get(0);
                        verify(mockDoc, times(1)).add(any(Paragraph.class));

                        // 验证Paragraph的属性设置是否都执行了
                        Paragraph mockPara = mockedParagraph.constructed().get(0);
                        verify(mockPara, times(1)).setFont(any());
                        verify(mockPara, times(1)).setFontSize(anyFloat());
                        verify(mockPara, times(1)).setMarginTop(anyFloat());

                        // 如果你需要验证返回的字节数组,因为我们Mock了流操作,这里会是空数组,根据你的需求调整
                    }
                }
            }
        }
    }
}

3. 关键细节说明

  • Fluent风格方法的Mock:iText的很多方法(比如setFont()、setFontSize())是链式调用,所以Mock的时候要让这些方法返回Mock实例本身,不然业务代码里的链式调用会报NPE。
  • 嵌套Mock的简化:如果觉得多层try-with-resources太啰嗦,可以把这些Mock逻辑抽成一个@BeforeEach方法,或者用Mockito的MockedConstruction的批量Mock(不过多层嵌套更清晰)。
  • 覆盖所有iText对象:如果你的代码里用到了Div或者其他iText元素,只需要给它们加对应的mockConstruction()就行,逻辑和Paragraph完全一致。
  • 许可证完全不触发:因为所有iText的实例都是Mock的,根本不会走到iText的实际代码,所以完全不会触发商业许可证的校验逻辑。

最后总结

完全不需要花太多精力就能实现!这个方案的核心就是用Mockito的构造函数Mock能力,把所有iText的依赖都替换成Mock对象,让测试完整走一遍你的业务代码,同时验证你对iText API的调用是否符合预期——既覆盖了代码,又规避了商业许可证的问题。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 11:49:54