【问题标题】:Mockito - Zero interactions with mock - SingletonMockito - 与模拟的零交互 - 单例
【发布时间】:2016-12-15 06:33:48
【问题描述】:

前提条件

我有以下课程(虚构,仅用于演示问题):

public class MySingleton {

  private static MySingleton sMySingleton;
  private static List<String> sItemList;

  private MySingleton(List<String> list) {
      sItemList = list;
  }

  public static MySingleton getInstance(List<String> list) {
      if (sMySingleton == null) {
          sMySingleton = new MySingleton(list);
      }
      return sMySingleton;
  }

  public void addItem(String item) {
      sItemList.add(item);
  }

  public void removeItem(String item) {
      sItemList.remove(item);
  }
}

以及相应的测试类:

public class MySingletonTest {

  private MySingleton mInstance;
  private List<String> mList;

  @Before
  public void setup() {
      mList = mock(List.class);
      mInstance = MySingleton.getInstance(mList);
  }

  @Test
  public void testAddItem() throws Exception {
      String item = "Add";
      mInstance.addItem(item);
      verify(mList, times(1)).add(item);
  }

  @Test
  public void testRemoveItem() throws Exception {
      String item = "Remove";
      mInstance.removeItem(item);
      verify(mList, times(1)).remove(item);
  }
}

问题

如果我现在执行完整的测试类,Mockito 会告诉我测试testRemoveItem() 与模拟有 0 次交互。

这怎么可能?


注意:
请不要开始讨论感觉单例。 这个问题是关于 Mockito 以及为什么它不起作用。

【问题讨论】:

  • 我从未使用过mockito,但正如我在Unitests 中所知道的,每个@Test 方法都是从头开始的。我认为您需要在 testRemoveItem 测试中删除它之前添加一项
  • 即使我不添加项目,至少应该使用verify调用和识别该方法

标签: java unit-testing singleton mockito


【解决方案1】:

JUnit 为每个测试创建一个新的测试类实例,Mockito 为每个测试填充一个新的模拟实例。但是,您的单例只初始化一次,这意味着在第一次测试期间mList == MySingleton.sItemList 而在之后的每个测试中mList != MySingleton.sItemList

换句话说,交互正在发生,但到第二次测试时,您检查的是错误的模拟。

虽然我知道您不是来讨论这种类型的单例的优点,但请记住,如果您这样做,您可能很难在测试中替换实例。相反,请考虑使单例的构造函数(仅)可用于您的测试,并将List(或其他状态)保留在实例中。这样您就可以为每个单独的测试创建一个全新的“Singleton”。

【讨论】:

  • 这听起来很合理。这意味着一个受保护的或包本地构造函数并从列表成员变量中删除静态修饰符?
  • 好的,做了一个快速测试,列表可以保持静态,只需将构造函数更改为受保护,使类成为最终类并在 setup 方法中以这种方式实例化它会得到预期的结果。谢谢!
  • 是的。只要addItemremoveItem 是实例方法,它们就应该与实例状态交互,即使您试图保证在生产中永远只有一个实例。如果您希望sItemList 是静态的,则根本不需要使该类可实例化。
  • @JDC Singletons 使使用它的所有代码基本上无法测试。而你的情况更糟,因为它有一个设计问题:第二个调用者试图获取一个带有自己列表的实例被简单地忽略了,这使得设计更加有问题。你真的应该考虑避免使用单例反模式,而改用依赖注入(它允许使用单例,但使用它们的代码非常可测试)。
  • 你是对的,将列表作为静态列表没有任何意义,我已经监督了这一点。事实上,这使得整个设计有问题,就像你正确提到的那样,第二个调用者不会用他的列表获取类,因为单例已经被实例化了。这其实是我第一眼真的完全没有看到的一点。非常感谢!
猜你喜欢
  • 2019-11-07
  • 2019-12-06
  • 1970-01-01
  • 2012-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多