1

我是单元测试的新手,并试图学习正确的风格。我喜欢设置我正在测试的对象,这样我就可以像在使用它一样测试它,而不是只测试一个新构建的对象。我无法测试从空对象中删除东西,因为构造时有很多对象。

以下面的例子为例,其中 ObservedList 正在被测试,ListListener 是一个被模拟的必要类。

public final class ObservedListTest {
  private ListListener<Integer> listener;
  private ObservedList<Integer> list;
  @BeforeMethod public void setup() {
    listener = mock(ListListener.class);
    list = new ObservedList<Integer>(listener);
    list.addAll(Arrays.asList(1,2,3));
    reset(listener);
  }
  @Test public void addFirst() {
    list.add(0, -1);
    verify(listener).listEdited(list, 0, 1, Collections.<Integer>emptyList());
    verifyNoMoreInteractions(listener);
  }
  @Test void addAtEnd() {
    list.add(9);
    verify(listener).listEdited(list, 3, 4, Collections.<Integer>emptyList());
    verifyNoMoreInteractions(listener);
  }
  @Test void removeMiddle() {
    list.remove(Integer.valueOf(2));
    verify(listener).listEdited(list, 1, 1, Collections.singletonList(2));
    verifyNoMoreInteractions(listener);
  }
}

作为一个新手,这对我来说似乎很有效,但我知道这是不好的做法,因为它使用了这种reset方法。我打电话reset是因为我不希望实际测试由于设置中发生的交互而混淆。

javadoc forreset甚至没有告诉您该方法的作用,因为它正忙于告诉您不应该使用它。通常我会简单地接受这个建议并reset通过删除我的setup方法并调整我的测试看起来更像这样来避免:

  @Test void removeMiddle() {
    listener = mock(ListListener.class);
    list = new ObservedList<Integer>(listener);
    list.addAll(Arrays.asList(1,2,3));
    list.remove(Integer.valueOf(2));
    InOrder inOrder = inOrder(listener);
    inOrder.verify(listener).listEdited(list, 0, 3,
      Collections.<Integer>emptyList());
    inOrder.verify(listener).listEdited(list, 1, 1,
      Collections.singletonList(2));
    verifyNoMoreInteractions(listener);
  }

这对我来说似乎也很有效。问题是该reset方法的文档说:

请考虑在冗长、过度指定的测试中编写简单、小型和集中的测试方法,而不是 reset()。

我尊重 Mockito 旨在鼓励单元测试中的良好风格,我想从中学习,但我很难理清它试图向我发送的信息。当我从我的测试中删除reset时,我的测试变得复杂、冗长且注意力集中,所以很明显我做错了。

做对了是什么样的?

4

2 回答 2

2

删除reset. 删除verifyNoMoreInteractions. 问题解决了。您的测试更简单、更短且更专注。

于 2013-10-18T04:43:21.970 回答
1

Mockito 2.1 添加了一个clearInvocations方法,可以这样使用:

public final class ObservedListTest {
  private ListListener<Integer> listener;
  private ObservedList<Integer> list;

  @BeforeMethod public void setup() {
    listener = mock(ListListener.class);
    list = new ObservedList<Integer>(listener);
    list.addAll(Arrays.asList(1,2,3));
    clearInvocations(listener);
  }

  @Test public void addFirst() {
    list.add(0, -1);
    verify(listener, only()).listEdited(list, 0, 1, Collections.<Integer>emptyList());
  }

  @Test void addAtEnd() {
    list.add(9);
    verify(listener, only()).listEdited(list, 3, 4, Collections.<Integer>emptyList());
  }

  @Test void removeMiddle() {
    list.remove(Integer.valueOf(2));
    verify(listener, only()).listEdited(list, 1, 1, Collections.singletonList(2));
  }
}

请注意,clearInvocationsjavadoc 还指出:

不惜一切代价尽量避免这种方法。如果您无法有效地测试您的程序,请仅清除调用。

于 2018-06-11T09:02:58.230 回答