我在使用 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。有关可能导致此问题的任何见解或建议差异将不胜感激。谢谢!
场景中不能嘲笑 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");
}