如何为私有final匿名内部类的方法编写TestNG测试用例?
测试私有final匿名内部类方法的实操思路
先理清楚你的场景:Formatter类里有个私有final的匿名内部类SampleFormatter实例,要测试它的formatDouble和formatBoolean方法,同时提升测试覆盖率。下面给你几个可行的实现方案:
方案一:用反射直接突破访问限制(最直接的野路子)
因为目标实例是私有+final的,常规方法拿不到,就得靠反射硬刚:
- 找到
Formatter类里的sampleFormatter字段,设置setAccessible(true)绕开私有访问限制。 - 因为字段是final的,还要额外修改
Field的modifiers属性,把final标记去掉(Java 9+要注意模块访问,但测试环境一般能绕过)。 - 拿到
SampleFormatter实例后,直接通过反射调用它的formatDouble和formatBoolean方法,传不同参数验证结果就行。
TestNG代码示例
import org.testng.Assert; import org.testng.annotations.Test; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.text.DecimalFormat; public class FormatterTest { @Test public void testFormatDouble() throws Exception { Formatter formatter = new Formatter(); // 拿到私有字段 Field field = Formatter.class.getDeclaredField("sampleFormatter"); field.setAccessible(true); // 去掉final修饰符 Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(field, field.getModifiers() & ~java.lang.reflect.Modifier.FINAL); // 获取内部类实例 Object sampleFormatter = field.get(formatter); // 拿到formatDouble方法 Method formatDoubleMethod = sampleFormatter.getClass().getDeclaredMethod("formatDouble", int.class, double.class, int.class); formatDoubleMethod.setAccessible(true); // 测NaN场景 String resultNaN = (String) formatDoubleMethod.invoke(sampleFormatter, 1, Double.NaN, 2); Assert.assertEquals(resultNaN, "NaN"); // 测0值场景 String resultZero = (String) formatDoubleMethod.invoke(sampleFormatter, 1, 0.0D, 2); Assert.assertEquals(resultZero, "0"); // 测正常数值(这里假设df是Formatter的私有DecimalFormat字段,先替换成测试用的实例) Field dfField = Formatter.class.getDeclaredField("df"); dfField.setAccessible(true); DecimalFormat testDf = new DecimalFormat("#.##"); dfField.set(formatter, testDf); String resultNormal = (String) formatDoubleMethod.invoke(sampleFormatter, 1, 123.456, 2); Assert.assertEquals(resultNormal, "123.46"); } @Test public void testFormatBoolean() throws Exception { Formatter formatter = new Formatter(); Field field = Formatter.class.getDeclaredField("sampleFormatter"); field.setAccessible(true); Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(field, field.getModifiers() & ~java.lang.reflect.Modifier.FINAL); Object sampleFormatter = field.get(formatter); Method formatBooleanMethod = sampleFormatter.getClass().getDeclaredMethod("formatBoolean", int.class, boolean.class); formatBooleanMethod.setAccessible(true); // 这里根据DEFAULT_FORMATTER的实际返回逻辑写断言,比如假设它返回大写字符串 String resultTrue = (String) formatBooleanMethod.invoke(sampleFormatter, 1, true); Assert.assertEquals(resultTrue, "TRUE"); String resultFalse = (String) formatBooleanMethod.invoke(sampleFormatter, 1, false); Assert.assertEquals(resultFalse, "FALSE"); } }
方案二:用PowerMockito扩展Mockito能力(要mock依赖时用)
如果需要mockDEFAULT_FORMATTER或者df这类依赖,PowerMockito比纯反射更顺手,它能搞定静态字段、私有字段和匿名类:
- 用
Whitebox工具类直接拿私有字段,不用自己处理final修饰符。 - 对于
DEFAULT_FORMATTER这种静态依赖,直接mock它的方法,验证调用逻辑。
PowerMockito+TestNG代码示例
import org.powermock.api.mockito.PowerMockito; import org.powermock.reflect.Whitebox; import org.testng.Assert; import org.testng.annotations.Test; import org.powermock.core.classloader.annotations.PrepareForTest; // 替换成DEFAULT_FORMATTER的实际类名 @PrepareForTest({Formatter.class, DefaultFormatter.class}) public class FormatterPowerMockTest { @Test public void testFormatBooleanWithMock() throws Exception { Formatter formatter = new Formatter(); // 直接拿到内部类实例 SampleFormatter sampleFormatter = Whitebox.getInternalState(formatter, SampleFormatter.class); // mock DEFAULT_FORMATTER的formatBoolean方法 PowerMockito.mockStatic(DefaultFormatter.class); PowerMockito.when(DefaultFormatter.DEFAULT_FORMATTER.formatBoolean(1, true)).thenReturn("CUSTOM_TRUE"); PowerMockito.when(DefaultFormatter.DEFAULT_FORMATTER.formatBoolean(1, false)).thenReturn("CUSTOM_FALSE"); // 调用方法验证结果 String resultTrue = sampleFormatter.formatBoolean(1, true); Assert.assertEquals(resultTrue, "CUSTOM_TRUE"); String resultFalse = sampleFormatter.formatBoolean(1, false); Assert.assertEquals(resultFalse, "CUSTOM_FALSE"); } }
方案三:重构代码(长期最优解)
反射虽然能解决问题,但后续维护起来麻烦,不如直接重构代码降低测试复杂度:
- 把匿名内部类的实现抽成独立的静态内部类或者顶级类,设置成包私有(默认访问权限),测试类和生产类同包就能直接访问。
- 或者在
Formatter里加个包私有的方法,返回sampleFormatter实例,专门给测试用。 - 这样不用依赖反射,测试代码简洁,还不影响生产代码的封装性。
重构示例
public class Formatter{ private final SampleFormatter sampleFormatter = new ConcreteSampleFormatter(); // 包私有方法,仅测试用 SampleFormatter getSampleFormatter() { return sampleFormatter; } // 抽成独立的静态内部类 static class ConcreteSampleFormatter implements SampleFormatter { @Override public String formatDouble (int tag, double Value, int pre) { if (Double.isNaN(Value)) { return "NaN"; } if (Value == 0.0D) { return "0"; } else { return df.format(Value); } } @Override public String formatBoolean(int i,boolean b){ return DEFAULT_FORMATTER.formatBoolean(i,b); } } }
测试时直接调用formatter.getSampleFormatter()拿到实例,正常测试方法就行。
内容的提问来源于stack exchange,提问作者Keerthi Senthil
相关产品推荐
相关产品推荐

