Maven Surefire插件负责执行项目中的单元测试,它默认会在test生命周期阶段启动一个独立的JVM来运行测试用例。很多乱码问题并非发生在测试逻辑内部,而是发生在这个独立JVM与Maven主进程之间的输出编码不一致上。如果在Windows系统上使用默认的GBK编码,而测试类名、方法名或断言消息中包含中文、✓、✗等特殊字符,控制台和测试报告就很容易出现问号或乱码。

要彻底解决,需要先理解一个关键点:Maven本身运行在一个JVM中,Surefire又会fork出一个或多个测试JVM。两个JVM的默认字符集可能不一样,测试输出从测试JVM传回Maven进程时如果编码不匹配,就会出现乱码。因此只调整一个地方往往不够,需要从项目配置、插件参数、运行环境三个层面同时考虑。
一、先定位乱码发生在哪一层
Maven Surefire相关的乱码通常出现在三个位置:Maven控制台输出、Surefire生成的txt或xml报告、以及IDE中的测试运行面板。不同位置的乱码成因可能不同,解决方式也有差异。如果控制台乱码但报告中文字正常,说明问题主要出在控制台编码;如果报告中也是乱码,则说明测试JVM写入文件时使用的编码与读取端不一致。
在Windows下,Maven启动时可能会继承系统的活动代码页,例如中文版Windows默认是GBK代码页936。而很多现代项目源文件使用UTF-8编码,如果测试JVM没有显式指定file.encoding,它也可能采用系统默认编码。此时即便源码本身正确,输出阶段仍可能产生乱码。可以用mvn -version查看Maven运行时信息,也可以通过一个简单测试打印Charset.defaultCharset()来确认测试JVM实际使用的默认字符集。
还有一个容易被忽略的因素是Surefire插件版本。旧版插件在参数传递和编码处理上存在一些默认行为差异,如果项目仍在使用较老的Surefire版本,建议优先升级到较新的稳定版本,部分乱码会在升级后自动消失。但插件升级并不能解决所有环境差异问题,尤其是Windows控制台编码和CI容器中缺少UTF-8 locale的情况。
二、统一项目源文件与资源编码
Maven项目应首先在pom中声明统一编码,这是解决乱码的基础。常用做法是在<properties>中设置project.build.sourceEncoding和project.reporting.outputEncoding。前者影响编译和资源处理,后者影响报告输出。虽然这两个属性并不能完全控制Surefire测试JVM的编码,但它们能保证源码编译、资源复制等环节使用UTF-8,避免乱码从源头产生。
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
</properties>
上述配置解决的是Maven主进程编译阶段的问题,但Surefire执行测试时会启动新的JVM,该JVM不会自动继承project.build.sourceEncoding。所以很多项目只配置了这些属性,测试输出中的中文或特殊符号仍然乱码。此时就需要进入Surefire插件自身的配置,为测试JVM显式指定启动参数。
如果项目中有资源文件包含中文内容,还可以在<build>中为resources插件设置encoding为UTF-8。不过对于Surefire输出乱码,资源插件配置只是辅助,核心仍需解决测试JVM的编码传递问题。
三、为Surefire测试JVM显式设置编码参数
Surefire插件允许通过<argLine>向测试JVM传递启动参数。将-Dfile.encoding=UTF-8写入argLine,可以强制测试JVM使用UTF-8作为默认字符集。配置方式如下:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<argLine>-Dfile.encoding=UTF-8</argLine>
</configuration>
</plugin>
如果项目已经使用了JaCoCo等插件,argLine中可能已经有其他参数。直接覆盖argLine会导致覆盖率统计等参数丢失。这时可以使用Surefire的late property replacement语法,将已有参数与编码参数合并。常见的做法是配置<argLine>@{argLine} -Dfile.encoding=UTF-8</argLine>,这样Surefire会先读取其他插件生成到argLine属性中的内容,再追加编码参数。
<configuration>
<argLine>@{argLine} -Dfile.encoding=UTF-8</argLine>
</configuration>
除了argLine,还可以使用systemPropertyVariables传递系统属性。不过需要明确的是,file.encoding是JVM启动时读取的敏感属性,通过系统变量方式在JVM启动后设置,对默认字符集的影响通常不如启动参数可靠。因此如果目标是解决输出乱码,优先使用argLine或forkedProcessExitTimeoutInSeconds附近的JVM参数配置。
设置完成后可以运行mvn clean test,观察控制台和target/surefire-reports目录下的报告文件。如果统一为UTF-8后,原先的乱码通常会变为正常中文和特殊符号。但如果Windows控制台仍使用GBK代码页,则控制台显示可能依旧异常,这是因为终端本身的解码方式尚未调整。
四、处理Windows控制台与CI环境差异
Windows下的终端编码是导致乱码反复出现的关键因素之一。即便Maven和Surefire都已经使用UTF-8,如果cmd或PowerShell窗口仍以GBK解码输出,看到的内容仍然可能是乱码。可以在执行测试前将控制台切换到UTF-8代码页,命令为chcp 65001。也可以在执行Maven命令前设置环境变量MAVEN_OPTS,让Maven主JVM也使用UTF-8编码。
set MAVEN_OPTS=-Dfile.encoding=UTF-8 mvn clean test
如果使用PowerShell,可以执行chcp 65001切到UTF-8,并通过$env:MAVEN_OPTS="-Dfile.encoding=UTF-8"设置环境变量。对于IntelliJ IDEA或Eclipse等IDE,可以在Maven Runner配置中增加VM Options为-Dfile.encoding=UTF-8,同时将IDE的编码设置统一为UTF-8,以避免IDE内置终端和Maven输出编码不一致。
在CI环境中,Linux容器通常默认使用UTF-8,但有些精简镜像的locale未配置,可能导致file.encoding为ANSI_X3.4-1968或C,这时也会出现特殊字符异常。可以在CI脚本中先执行export LANG=C.UTF-8或export LC_ALL=C.UTF-8,再运行Maven测试。对于Windows Server环境,建议将系统区域设置调整为UTF-8,或在执行步骤中显式设置代码页和MAVEN_OPTS。
排查时可以在测试类中临时添加打印语句,输出System.getProperty("file.encoding")与Charset.defaultCharset().name(),确认测试JVM实际使用的编码。若这两个值已经为UTF-8但乱码仍存在,再检查控制台代码页和报告文件读取方式。通常经过项目属性、Surefire参数、终端环境三层调整后,Maven Surefire输出中的特殊字符乱码问题可以得到稳定解决。

