3

我想为单例类编写单元测试,但是这个类依赖于 ui 组件。该类是PageManager并且具有一些功能可以在页面历史记录中返回和前进。通过单元测试,我喜欢测试这个历史功能,但我不想初始化 ui 的东西,因为这个测试不需要它。

我是 JMockit 的新手,我试过了,但没有成功:这是要模拟的原始类:

public final class PageManager {
  private static final PageManager INSTANCE = new PageManager();

  private final Set<Page> pages = new HashSet<>();
  private Page currentPage;
  private boolean initialized = false;

  private PageManager() {
    // Do some UI stuff
  }

  public static PageManager getInstance() {
    return INSTANCE;
  }

  public void addPage(final Page page) {
    pages.add(page);
  }

  public void initialize() {
    // Do some UI stuff
    initialized = true;
  }

  public Page getPage() { return currentPage; }
  public void setPage(final Page page) { ... }

  public void goBack() { ... };
  public void goForward() { ... };
  public boolean canGoBack() { ... };
  public boolean canGoForward() { ... };

  private void activatePage(final Page page) {
    // Do some UI stuff
    this.currentPage = page;
  }

  private void deactivatePage(final Page page) {
    // Do some UI stuff
  }
}

这是模拟版本:

public final class MockedPageManager extends MockUp<PageManager> {
  PageManager instance;

  @Mock
  void $init(final Invocation invocation) {
    instance = invocation.getInvokedInstance();
  }

  @Mock
  void initialize() {
    // Don't do UI stuff
    Deencapsulation.setField(instance, "initialized", true);
  }

  @Mock
  void activatePage(Page page) {
    Deencapsulation.setField(instance, "currentPage", page);
    page.activate();
  }

  @Mock
  void deactivatePage(Page page) {
  }
}

还有一个小测试:

@Test
public void testGoBack() {
  new MockedPageManager();

  final Page p1 = new Page() { @Override public String getTitle() { return "p1"; } };
  final Page p2 = new Page() { @Override public String getTitle() { return "p2"; } };

  final PageManager pm = PageManager.getInstance();
  pm.addPage(p1);
  pm.addPage(p2);
  pm.initialize();

  pm.setPage(p1)
  assertEquals(p1, pm.getCurrentPage());
  pm.setPage(p2);
  assertEquals(p2, pm.getCurrentPage())
  assertTrue(pm.canGoBack());
  pm.goBack();
  assertEquals(p1, pm.getCurrentPage());
}

在这个测试中,该$init方法被 JMockit 正确调用。问题是,在调用NullPointerExceptions时会在测试中抛出an 。pm.addPage(p1)堆栈跟踪说,NPE 出现在原始类中,因为PageManager该字段Set pagesnull.

我的问题是:这个单例类是否正确模拟了?该方法是否$init仅覆盖构造函数或 Java 实例初始化程序,即 Set pages = new HashSet<>();

4

1 回答 1

1

如此处所述,实例初始化块或语句被复制到每个构造函数中(由编译器)。我怀疑 JMockit 使用反射/字节码操作来模拟类的构造函数,有效地绕过所有初始化代码。因此初始化器不会被执行并且设置变量保持为空。如果您真的必须完成这项工作,请尝试在模拟中正确初始化它。更好的是,重构你的类以允许它在测试中使用(例如,一个额外的包私有构造函数用于测试注入的依赖项;或者将页面历史功能移动到它自己的类)。

于 2013-09-18T10:50:41.797 回答