使用JUnit测试Java中MultipartFile接口时出现空指针异常求助
控制器单元测试空指针异常排查与修复
问题场景
我要为一个在Postman中可正常运行的接口编写单元测试,接口代码如下:
@PostMapping("/uploadGText") public ResponseEntity<Object> translateGt(@RequestParam("text") MultipartFile file, @RequestParam("langI") String langI, @RequestParam("langO") String langO) throws IOException{ String fileName = fileStorageService.storeFile(file); String path = "Uploads\\"+fileName; TxtFile tFile = new TxtFile(); tFile.getPath(path,langI,langO); return downloadFile(tFile.getNewPath()); }
当前的单元测试代码如下,想要实现参数传递并完成断言:
@Test public void shouldExecuteTranslateController() throws IOException { Path path = Paths.get("Uploads\\testText.txt"); String name = "text"; String originalFileName = "testText.txt"; String contentType = "text/plain"; byte[] content = null; try { content = Files.readAllBytes(path); } catch (final IOException e) { } MultipartFile result = new MockMultipartFile(name, originalFileName, contentType, content); TranslateController tr = new TranslateController(); tr.translateGt(result,"en","es"); //assertEquals("textTet", tr.translateGt(result,"en","es");) }
执行测试时抛出空指针异常,异常栈如下:
java.lang.NullPointerException at com.jalasoft.convert.controller.endpoint.TranslateController.translateGt(TranslateController.java:32) at com.jalasoft.convert.controller.endpoint.TranslateControllerTest.shouldExecuteTranslateController(TranslateControllerTest.java:34) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
问题根源
空指针出在控制器第32行的fileStorageService.storeFile(file)代码——你直接new TranslateController()创建实例时,控制器依赖的fileStorageService成员变量没有被注入,处于null状态,和MultipartFile本身无关。
修复方案
方案1:用Spring Test框架注入控制器(推荐)
借助Spring的测试框架,让容器帮你注入依赖,同时用MockMvc模拟真实HTTP请求,更贴合接口实际运行场景:
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import java.nio.file.Files; import java.nio.file.Paths; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @RunWith(SpringRunner.class) @WebMvcTest(TranslateController.class) public class TranslateControllerTest { @Autowired private MockMvc mockMvc; @Test public void shouldExecuteTranslateController() throws Exception { MockMultipartFile testFile = new MockMultipartFile( "text", "testText.txt", "text/plain", Files.readAllBytes(Paths.get("Uploads\\testText.txt")) ); mockMvc.perform(multipart("/uploadGText") .file(testFile) .param("langI", "en") .param("langO", "es")) .andExpect(status().isOk()); // 可扩展断言:验证返回文件的内容、响应头(如Content-Disposition)等 } }
方案2:手动Mock依赖
如果不想启动Spring容器,用Mockito手动mockfileStorageService并注入控制器:
import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.http.ResponseEntity; import java.nio.file.Files; import java.nio.file.Paths; import static org.mockito.Mockito.when; import static org.junit.Assert.assertEquals; @RunWith(MockitoJUnitRunner.class) public class TranslateControllerTest { @Mock private FileStorageService fileStorageService; // 替换为你的实际服务类名 @Test public void shouldExecuteTranslateController() throws Exception { MockMultipartFile testFile = new MockMultipartFile( "text", "testText.txt", "text/plain", Files.readAllBytes(Paths.get("Uploads\\testText.txt")) ); // Mock文件存储方法的返回值 when(fileStorageService.storeFile(testFile)).thenReturn("testText.txt"); TranslateController controller = new TranslateController(); // 手动注入Mock服务(需给控制器添加setter方法,或用反射注入) controller.setFileStorageService(fileStorageService); ResponseEntity<Object> response = controller.translateGt(testFile, "en", "es"); // 断言响应状态码 assertEquals(200, response.getStatusCodeValue()); } }
额外注意事项
- 不要忽略IO异常:测试代码里的空try-catch会导致文件读取失败时
content为null,应该让测试直接失败,避免后续执行无效代码 - 完善断言逻辑:根据
downloadFile的返回值,添加对文件内容、响应头的断言,提升测试的有效性
内容的提问来源于stack exchange,提问作者Gabriel Valda Arraya
相关产品推荐
相关产品推荐

