Mockito ArgumentCaptor在verify()时会导致NullpointerException

7

我目前正在尝试为我们的Servlet创建测试。 我已经模拟了HttpServletRequestHttpServletResponse和充当数据库处理程序的对象。 在doPost方法的测试中,我的愿望是比较来自Json对象和发送到数据库对象的ArrayList的值。

这个Servlet:

public class WidgetStatusServlet extends HttpServlet {

private DBController db = new DBController();

    @Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    response.setContentType("application/json");
    JsonParser parser = new JsonParser();
    JsonElement tradeElement = parser.parse(request.getParameter("widgetdata"));
    JsonArray json = tradeElement.getAsJsonArray();
    Gson gson = new Gson();
    Type listType = new TypeToken<List<WidgetStatus>>() {
    }.getType();
    ArrayList<WidgetStatus> widgets = gson.fromJson(json, listType);

    Iterator<WidgetStatus> iterator = widgets.iterator();
    System.out.println("Widgets");
    while (iterator.hasNext()) {
        WidgetStatus node = (WidgetStatus) iterator.next();
        System.out.println(node);
    }

    db.addWidgetStatus(widgets);

}

测试内容:

public class WidgetStatusServletTest {

@Captor
private ArgumentCaptor<ArrayList<WidgetStatus>> captor;

private DBController dbMock = mock(DBController.class);
private HttpServletRequest request = mock(HttpServletRequest.class);
private HttpServletResponse response = mock(HttpServletResponse.class);
Repository repository = mock(Repository.class);`


    @Test
public void doPost_ShouldVerifyWidgetsIsProvided() throws Exception {
    final WidgetStatus widget1 = new WidgetStatus("id1", "div_id1", "5", "5", "1", "2", false);
    final WidgetStatus widget2 = new WidgetStatus("id2", "div_id2", "2", "1", "3", "1", true);

    when(request.getParameter("widgetdata")).thenAnswer(new Answer<String>() {
        @Override
        public String answer(InvocationOnMock invocation) throws Throwable {
            ArrayList<WidgetStatus> array = new ArrayList<WidgetStatus>();
            array.add(widget1);
            array.add(widget2);
            String json = new Gson().toJson(array);
            return json;
        }
    });
    new WidgetStatusServlet().doPost(request, response);
    verify(dbMock).addWidgetStatus(captor.capture());
    assertNotNull(captor.getAllValues());


}

当我运行测试时,代码在这一行报错:NullpointerException

        verify(dbMock).addWidgetStatus(captor.capture());

我做错了什么?我看了几个相同使用ArgumentCaptor的例子。也许这只是一个小错误?
5个回答

12
如果您使用MockitoJUnitRunner来运行测试,那么将为您创建一个ArgumentCaptor,您不需要显式初始化它。如果您这样做,您还可以使用@Mock注释创建模拟对象,也就是说,您不再需要mock静态方法了。您的测试类应该像这样开始。
@RunWith(MockitoJUnitRunner.class)
public class WidgetStatusServletTest {

    @Captor private ArgumentCaptor<ArrayList<WidgetStatus>> captor;
    @Mock private DBController mockController;
    @Mock private HttpServletRequest mockRequest;
    @Mock private HttpServletResponse mockResponse;
    @Mock private Repository mockRepository;

本问题讨论了是否使用MockitoJUnitRunner或显式调用MockitoAnnotations.initMocks的问题。

另外提一下 - 如果您使用清楚地指示您的对象是模拟对象的变量名称,那么您的测试方法将更易于阅读,就像我在这里所做的那样。否则,在具有许多不同变量的长测试类中,很容易失去对哪些变量引用模拟对象和哪些引用“真实”对象的跟踪。


好的,现在我在同一行得到了以下失败: 期望但未调用: mockController.addWidgetStatus( <捕获参数> ); -> 在com.mycompany.bachelorprosjekt2014.servlets.WidgetStatusServletTest.doPost_ShouldVerifyWidgetsIsProvided(WidgetStatusServletTest.java:70) 实际上,这个模拟没有任何交互。 - frods
你的问题在于WidgetStatusServlet有自己的DBController,与你创建的模拟控制器不同。如果你想让这个测试工作,你需要提供一种方法将你的模拟DBController注入到WidgetStatusServlet中。 - Dawood ibn Kareem
我想我明白了。所以您的意思是在调用doPost()之前必须将mockController注入到servlet中? - frods

6

@Mock@Captor(以及@Spy@InjectMocks)标记的字段将由Mockito自动初始化,但仅当您在测试类中调用MockitoAnnotations.initMocks(this)时才会进行初始化。

您可以在setUp(JUnit3)或@Before方法(JUnit4)中自行执行此操作,或使用@RunWith(MockitoJUnitRunner.class)注释您的测试用例以获取自动初始化和验证。


5

看起来ArgumentCaptor没有被初始化。

使用mockito注释时,它们会通过以下方式进行初始化:

  • Adding the MockitoAnnotations.initMocks(this); line in the setup method

     @Before
     public void setUp() throws Exception {
    
         MockitoAnnotations.initMocks(this);
     }
    
  • Or by adding @RunWith(MockitoJUnitRunner.class) annotation to the TestClass.


1
如果您正在使用Junit 5 jupitor和注释,如@Spy @Captor和@Mock无法正常工作,则是因为"RunWith"已被删除,现在需要使用扩展名使用"@ExtendWith"注释。
因此,使用@ExtendWith(MockitoExtension.class)而不是@RunWith(MockitoJUnitRunner.class)将为您解决问题,并且注释将按照预期工作。

0

您没有初始化对象,因此无法执行其capture()方法。在调用其方法之前,必须先初始化一个对象。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接