You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Mockito测试Servlet时BO模拟行为未执行的问题排查

问题原因分析
  • 核心问题:Servlet直接实例化MyBO,Mock对象未生效
    Servlet的doGet方法里写的是new MyBO().getAllItems(...),每次调用都会生成全新的MyBO实例,和测试中Mock的myBO完全是两个独立对象。Mockito的Mock仅对创建的那个Mock实例有效,自然无法拦截真实BO的数据库查询逻辑。
  • 参数匹配失败
    测试中用eq(myVO)匹配VO参数,但Servlet里是自己new的MyVO,和测试中的myVO不是同一个对象——就算Mock对象能被用上,这个参数匹配也会失败,导致Mock逻辑不会触发。
解决建议

方案1:重构Servlet做依赖注入(推荐)

把MyBO改成Servlet的依赖,而非在方法内直接new,这样测试时就能替换成Mock对象。

修改Servlet代码:

public class MyServlet extends HttpServlet {
    private MyBO myBO;

    // 无参构造,兼容容器初始化
    public MyServlet() {
        this.myBO = new MyBO();
    }

    // 测试用构造函数,注入Mock的MyBO
    public MyServlet(MyBO myBO) {
        this.myBO = myBO;
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        MyVO myVO = new MyVO();
        try (Connection connection = DBConnectionPooler.getDatabaseConnection("DBNAME")) {
            RequestDispatcher view;
            // 使用注入的myBO实例
            myBO.getAllItems(connection, myVO);
            request.setAttribute("myList", myVO.getMyList());
            view = request.getRequestDispatcher("/someJSP.jsp");
            view.forward(request, response);
        } catch (Exception e) {
            logger.error(e);
        }
    }
}

修改测试代码:

@Test
void testGetRequest() {
    try {
        RequestDispatcher rd = mock(RequestDispatcher.class);
        HttpServletRequest request = mock(HttpServletRequest.class);
        HttpServletResponse response = mock(HttpServletResponse.class);
        MyBO myBO = mock(MyBO.class);

        // 用带参构造初始化Servlet,传入Mock的MyBO
        MyServlet myServlet = new MyServlet(myBO);

        List<TestObject> testList = new ArrayList<>();
        testList.add(new TestObject());
        testList.add(new TestObject());

        // 改用any(MyVO.class)匹配,因为Servlet会自己new VO
        doAnswer(invocation -> {
            MyVO arg = invocation.getArgument(1);
            arg.setMyList(testList);
            return null;
        }).when(myBO).getAllItems(any(Connection.class), any(MyVO.class));

        when(request.getRequestDispatcher("/someJSP.jsp")).thenReturn(rd);

        myServlet.doGet(request, response);

        verify(request).setAttribute("myList", testList);
        verify(rd).forward(request, response);
    } catch (IOException | ServletException e) {
        e.printStackTrace();
        fail();
    }
}

方案2:用@InjectMocks注入Mock(适合小改场景)

如果没法修改构造函数,把MyBO改成Servlet的成员变量,再用Mockito的@InjectMocks自动注入Mock对象。

修改Servlet:

public class MyServlet extends HttpServlet {
    private MyBO myBO = new MyBO(); // 成员变量,而非局部new

    @Override
    protected void doGet(...) {
        // ...
        myBO.getAllItems(connection, myVO); // 使用成员变量
        // ...
    }
}

测试代码:

@ExtendWith(MockitoExtension.class)
class MyServletTest {
    @Mock
    private MyBO myBO;
    @Mock
    private HttpServletRequest request;
    @Mock
    private HttpServletResponse response;
    @Mock
    private RequestDispatcher rd;

    @InjectMocks
    private MyServlet myServlet;

    @Test
    void testGetRequest() throws IOException, ServletException {
        List<TestObject> testList = new ArrayList<>();
        testList.add(new TestObject());
        testList.add(new TestObject());

        doAnswer(invocation -> {
            MyVO arg = invocation.getArgument(1);
            arg.setMyList(testList);
            return null;
        }).when(myBO).getAllItems(any(Connection.class), any(MyVO.class));

        when(request.getRequestDispatcher("/someJSP.jsp")).thenReturn(rd);

        myServlet.doGet(request, response);

        verify(request).setAttribute("myList", testList);
        verify(rd).forward(request, response);
    }
}

额外优化:Mock数据库连接

当前测试会调用真实的数据库连接池方法,建议用Mockito的mockStatic拦截静态方法,避免依赖外部数据库:

try (MockedStatic<DBConnectionPooler> mockedPooler = mockStatic(DBConnectionPooler.class)) {
    // Mock数据库连接,避免真实调用
    mockedPooler.when(() -> DBConnectionPooler.getDatabaseConnection("DBNAME"))
                .thenReturn(mock(Connection.class));

    myServlet.doGet(request, response);

    // 后续验证逻辑
}

内容的提问来源于stack exchange,提问作者mohd sabahat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 06:48:27