JUnit测试中ImageUtil.scaleImage方法始终返回null问题求助
问题:ImageUtil工具类scaleImage方法JUnit测试返回null排查
我有一个带final私有构造方法的工具类ImageUtil,所有方法均为静态方法。在执行JUnit测试调用其scaleImage方法时,该方法始终返回null。以下是相关代码及堆栈跟踪信息,恳请协助排查解决。
工具类代码
public final class ImageUtil { final static Logger logger = LoggerFactory.getLogger(ImageUtil.class); private ImageUtil() {} private static final int MAX_MASTER_WIDTH = 2000; private static final int MIN_MASTER_WIDTH = 500; private static final int MAX_SAMPLE_WIDTH = 400; public static BufferedImage scaleImage(BufferedImage image, int maxWidth, int type){ int oldWidth = image.getWidth(); int oldHeight = image.getHeight(); int newHeight = (int) Math.round((double) maxWidth / (double) oldWidth * (double) oldHeight); BufferedImage resized = new BufferedImage(maxWidth, newHeight, type); Graphics2D g = resized.createGraphics(); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g.drawImage(image, 0, 0, maxWidth, newHeight, 0, 0, image.getWidth(), image.getHeight(), null); g.dispose(); return resized; } }
测试类代码
@PrepareForTest(value = {FileUtil.class,JpegReader.class,ImageUtil.class,System.class,FileUtils.class,JpegReader.class,ImageIO.class}) @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class ImageUtilTest { MockedStatic<ImageUtil> imageUtil= null ; File tempFile = null; @ClassRule TemporaryFolder temporaryFolder ; @BeforeEach public void init() throws IOException { imageUtil= mockStatic(ImageUtil.class); tempFile = mock(File.class); temporaryFolder = new TemporaryFolder(); temporaryFolder.create(); } @AfterEach public void afterEach() { imageUtil.close(); tempFile.delete(); temporaryFolder.delete(); } @Test @DisplayName("scaleImageTest") public void scaleImageTest() throws Exception{ int oldWidth = 200; int oldHeight = 150; int newHeight = (int) Math.round((double) 200 / (double) oldWidth * (double) oldHeight); BufferedImage mockedImage = mock(BufferedImage.class) ; BufferedImage mockedNewImage = mock(BufferedImage.class) ; Graphics2D mockedGraphics = mock(Graphics2D.class); when(mockedImage.getWidth()).thenReturn(oldWidth); when(mockedImage.getHeight()).thenReturn(oldHeight); when(mockedImage.getWidth()).thenReturn(200); Mockito.doNothing().when(mockedGraphics).setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); when(mockedGraphics.drawImage(mockedImage, 0, 0, 200, newHeight, 0, 0, oldHeight,oldWidth, null)).thenReturn(true); Mockito.doNothing().when(mockedGraphics).dispose(); PowerMockito.whenNew(BufferedImage.class).withArguments(oldWidth, oldHeight,newHeight).thenReturn(mockedNewImage); when(mockedNewImage.createGraphics()).thenReturn(mockedGraphics); BufferedImage actualImage = ImageUtil.scaleImage(mockedImage, oldWidth, newHeight); assertEquals(mockedNewImage, actualImage); } }
堆栈跟踪信息
org.opentest4j.AssertionFailedError: expected: <Mock for BufferedImage, hashCode: 307036850> but was: <null> at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55) at org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils.java:62) at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182) at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:177) at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1141) at test.com.renoworks.framework.util.ImageUtilTest.scaleImageTest(ImageUtilTest.java:234) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725) at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84) at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115) at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104) at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
问题原因及修复方案
核心问题
- 错误mock了目标工具类:测试中调用
mockStatic(ImageUtil.class),这会让所有静态方法调用默认返回null,而不是执行真实的方法逻辑。我们要测试scaleImage的真实实现,不需要mock整个类。 whenNew参数不匹配:工具类中创建BufferedImage的代码是new BufferedImage(maxWidth, newHeight, type),但测试中用withArguments(oldWidth, oldHeight,newHeight),参数顺序和值都不对,导致无法返回预期的mock对象。drawImage参数顺序错误:工具类中调用drawImage时,源宽高是image.getWidth(), image.getHeight(),但测试中写的是oldHeight,oldWidth,顺序颠倒,导致mock匹配失败。- 参数类型错误:原测试中调用
scaleImage时第三个参数传了newHeight,但该参数应该是BufferedImage的类型常量(如TYPE_INT_RGB),而非高度值。
修正后的测试代码
@PrepareForTest(ImageUtil.class) @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class ImageUtilTest { @TempDir TemporaryFolder temporaryFolder; @Test @DisplayName("scaleImageTest") public void scaleImageTest() throws Exception { int oldWidth = 200; int oldHeight = 150; int targetMaxWidth = oldWidth; int imageType = BufferedImage.TYPE_INT_RGB; int newHeight = (int) Math.round((double) targetMaxWidth / (double) oldWidth * (double) oldHeight); // Mock依赖对象 BufferedImage mockedImage = mock(BufferedImage.class); BufferedImage mockedNewImage = mock(BufferedImage.class); Graphics2D mockedGraphics = mock(Graphics2D.class); // 设置mock行为 when(mockedImage.getWidth()).thenReturn(oldWidth); when(mockedImage.getHeight()).thenReturn(oldHeight); when(mockedNewImage.createGraphics()).thenReturn(mockedGraphics); // 匹配BufferedImage的构造参数 PowerMockito.whenNew(BufferedImage.class) .withArguments(targetMaxWidth, newHeight, imageType) .thenReturn(mockedNewImage); // 匹配drawImage的参数顺序 doNothing().when(mockedGraphics).setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); when(mockedGraphics.drawImage(mockedImage, 0, 0, targetMaxWidth, newHeight, 0, 0, oldWidth, oldHeight, null)) .thenReturn(true); doNothing().when(mockedGraphics).dispose(); // 调用真实方法 BufferedImage actualImage = ImageUtil.scaleImage(mockedImage, targetMaxWidth, imageType); // 断言结果 assertEquals(mockedNewImage, actualImage); } }
额外优化点
- 使用JUnit 5的
@TempDir替代@ClassRule的TemporaryFolder,更符合JUnit 5规范。 - 移除了不必要的
mockStatic(ImageUtil.class)和无关的File mock,聚焦测试目标方法。 - 明确
imageType参数,避免用高度值代替类型常量的错误。
内容的提问来源于stack exchange,提问作者prabalpratap singh
相关产品推荐
相关产品推荐

