使用Mockito测试Servlet时测试类无法获取响应
解决Mockito测试Servlet无法捕获响应输出的问题
我来帮你搞定这个问题——这种情况大概率是因为你没正确处理ServletOutputStream的Mock逻辑,毕竟Mockito默认的Mock实例不会帮你记录写入流的内容,下面给你一步步拆解解决方案:
问题根源
ServletOutputStream是个抽象类,Mockito生成的默认Mock对象只会执行空实现,不会保留任何写入的数据。所以你在Servlet里调用write方法后,控制台能看到输出(那是你自己打印的日志),但测试代码里根本拿不到实际的响应内容。
解决方案一:手动用ByteArrayOutputStream捕获输出
我们可以自己实现一个ServletOutputStream,把写入的内容转存到ByteArrayOutputStream里,这样就能在测试里获取到响应了:
import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import javax.servlet.ServletOutputStream; import javax.servlet.ServletResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify; public class YourServletTest { @Mock private ServletResponse mockResponse; public YourServletTest() { MockitoAnnotations.openMocks(this); } @Test public void testCommitResponseBuffer() throws IOException { // 1. 准备用来存储响应内容的流 ByteArrayOutputStream capturedOutput = new ByteArrayOutputStream(); // 2. 自定义ServletOutputStream,将写入的内容转存到capturedOutput ServletOutputStream servletOutputStream = new ServletOutputStream() { @Override public void write(int b) throws IOException { capturedOutput.write(b); } // 按需实现其他可能用到的方法,比如flush/close @Override public void flush() throws IOException { capturedOutput.flush(); } @Override public void close() throws IOException { capturedOutput.close(); } // Servlet 3.1+ 要求实现的方法,默认返回true即可 @Override public boolean isReady() { return true; } @Override public void setWriteListener(WriteListener writeListener) { // 空实现,测试场景下一般用不到 } }; // 3. 让Mock的ServletResponse返回我们自定义的输出流 when(mockResponse.getOutputStream()).thenReturn(servletOutputStream); // 4. 调用Servlet的目标方法 YourServlet servlet = new YourServlet(); String testBuffer = "Hello, Mockito!"; servlet.commitResponseBuffer(testBuffer, mockResponse); // 5. 获取并验证响应内容 String actualResponse = capturedOutput.toString(StandardCharsets.UTF_8.name()); assertEquals(testBuffer, actualResponse); // 顺便验证ContentType是否正确设置 verify(mockResponse).setContentType("text/plain"); } }
解决方案二:用Spring Test的MockServletOutputStream(更简便)
如果你项目里已经引入了Spring Test依赖,可以直接用它提供的MockServletOutputStream,它内部已经封装了ByteArrayOutputStream,不用自己写抽象类实现:
import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.mock.web.MockServletOutputStream; import javax.servlet.ServletResponse; import java.io.IOException; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify; public class YourServletTest { @Mock private ServletResponse mockResponse; public YourServletTest() { MockitoAnnotations.openMocks(this); } @Test public void testCommitResponseBufferWithSpringMock() throws IOException { MockServletOutputStream mockServletOutputStream = new MockServletOutputStream(); when(mockResponse.getOutputStream()).thenReturn(mockServletOutputStream); YourServlet servlet = new YourServlet(); String testBuffer = "Hello, Spring Mock!"; servlet.commitResponseBuffer(testBuffer, mockResponse); // 直接获取响应内容 String actualResponse = mockServletOutputStream.getContentAsString(); assertEquals(testBuffer, actualResponse); verify(mockResponse).setContentType("text/plain"); } }
额外注意点
- 区分OutputStream和PrintWriter:如果你的Servlet里用的是
response.getWriter()而不是getOutputStream(),那要Mock的是getWriter()方法,用StringWriter来捕获输出。 - ZipOutputStream的处理:你代码里提到了
write to ZipOutp...,如果是用ZipOutputStream包装了ServletOutputStream,一定要确保在方法末尾调用zipOut.flush()和zipOut.close(),否则压缩的数据可能还留在缓冲区里,没写入到我们的捕获流中。 - 字符编码:获取字符串时指定正确的编码(比如UTF-8),避免出现乱码问题。
内容的提问来源于stack exchange,提问作者Jaydeep Bobade
相关产品推荐
相关产品推荐

