单元测试使用 Mockito 单元测试 Servlet:模拟行为未执行

问题描述 投票:0回答:1

我在使用 Mockito 为 servlet 编写单元测试时遇到问题。我尝试使用 doAnswer 模拟业务对象 (BO) 对象的行为,以将测试值设置为值对象 (VO) 对象。然而,似乎原始 BO 对象的行为仍在执行,而不是模拟的行为。这是我的 servlet 的 doGet 方法的简化版本:

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

MyVO myVO = new MyVO();

try (Connection connection = DBConnectionPooler.getDatabaseConnection("DBNAME")) {

    RequestDispatcher view;

    new MyBO().getAllItems(connection, myVO);

    request.setAttribute("myList", myVO.getMyList());

    view = request.getRequestDispatcher("/someJSP.jsp");

    view.forward(request, response);

} catch (Exception e) {

    logger.error(e);

}

}

这是我使用 Mockito 的测试方法的相关部分:

@Test
void testGetRequest() {

try {

    RequestDispatcher rd = mock(RequestDispatcher.class);

    HttpServletRequest request = mock(HttpServletRequest.class);

    HttpServletResponse response = mock(HttpServletResponse.class);

    MyBO myBO = mock(MyBO.class);



    MyVO myVO = new MyVO();

    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), eq(myVO));
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();
}

}

验证时出现问题

request.setAttribute("myList", testList)
。预期值是 testList,但实际值似乎来自实际数据库而不是模拟行为。我正在使用 Mockito 版本 5.11.0 和 JUnit 版本 5.10.2。有关可能导致此问题的任何见解或建议差异将不胜感激。谢谢!

java unit-testing servlets junit mockito
1个回答
0
投票

场景中不能嘲笑 MyBo。原因是这行代码:

new MyBO().getAllItems(connection, myVO);

这将始终创建一个真正的 MyBO。

你能做什么来解决这个问题?

要使用模拟,您需要有某种注入点。例如。使其成为 Servlet 的一个字段。然后您可以注入模拟而不是真正的 MyBO,您的 servlet 将调用模拟对象并执行模拟行为。

进一步解释一下:模拟对象仍然只是类的一个实例。它不会神奇地用模拟替换所有实例。它也不能改变类的构造函数来创建模拟。如果您调用该类的构造函数,您将始终获得真实事物的实例。这意味着在您的测试中,您需要将模拟实例注入您正在测试的类中。最简单的方法是通过构造函数或设置器。我缺少更多的代码来为您提供解决问题的工作方法,但这是一个简单的示例:

public class Example {

    public String testMe() {
        return new MyBo().doSomething();
    }

}

现在这又是不可测试的。

我们可以这样改变它:

public class Example {
    
    private MyBo myBo;

    public Example(MyBo myBo) {
        this.myBo = myBo;
    }

    public String testMe() {
        return new MyBo().doSomething();
    }

}

然后你像这样测试它

@Test
public void test() {
    MyBo mock = mock(MyBo.class);

    when(mock.doSomething()).thenReturn("mocked");

    Example underTest = new Example(mock);

    assertThat(underTest.testMe()).isEqualTo("mocked");
}
© www.soinside.com 2019 - 2024. All rights reserved.