14

我正在编写一个类似 TotalCommander 的应用程序。我有一个单独的文件列表组件和一个模型。CurrentDirChanged模型支持侦听器并以下列方式为诸如此类的事件发出通知:

私人无效fireCurrentDirectoryChanged(最终IFile dir){
    if (SwingUtilities.isEventDispatchThread())
        对于(FileTableEventsListener 监听器:tableListeners)
            listener.currentDirectoryChanged(dir);
    别的 {
        SwingUtilities.invokeLater(new Runnable() {
            公共无效运行(){
                对于(FileTableEventsListener 监听器:tableListeners)
                    listener.currentDirectoryChanged(dir);
            }
        });
    }
}

我为此写了一个简单的测试:

@测试
公共 void testEvents() 抛出 IOException {
    IFile testDir = mockDirectoryStructure();
    最终 FileSystemEventsListener 监听器 =
                context.mock(FileSystemEventsListener.class);
    context.checking(新期望(){{
        oneOf(listener).currentDirectoryChanged(with(any(IFile.class)));
    }});

    FileTableModel 模型 = new FileTableModel(testDir);
    模型.switchToInnerDirectory(1);
}

这不起作用,因为没有EventDispatchThread. 有没有办法在无头构建中对此进行单元测试?

单元测试 java swing jmock

4

5 回答 5

13

请注意,一般来说,对 UI 内容进行单元测试总是很困难,因为您必须模拟很多不可用的内容。
因此,开发应用程序(任何类型)的主要目标始终是尽可能地将 UI 内容与主应用程序逻辑分开。这里有很强的依赖关系,使单元测试变得非常困难,基本上是一场噩梦。这通常通过使用像MVC类型的方法这样的模式来利用,在这种方法中,您主要测试您的控制器类,而您的视图类除了构建 UI 并将它们的操作和事件委托给控制器之外什么都不做。这分离了职责并使测试更容易。

此外,您不必测试框架已经提供的东西,例如测试事件是否被正确触发。您应该只测试您自己编写的逻辑。

于 2009-09-26T09:46:03.970 回答
11

这个

FEST 是一个库集合,在Apache 2.0 许可下发布,其使命是简化软件测试。它由各种模块组成,可以与TestNGJUnit ...

于 2009-09-26T11:48:25.953 回答
2

检查 uispec4j 项目。这就是我用来测试我的用户界面的东西。

www.uispec4j.org

于 2009-09-29T11:40:52.870 回答
2

我认为测试的问题是揭示了代码的问题。决定它是否在调度线程中运行不应该是模型的工作,这是太多的责任。它应该只做它的通知工作,让调用组件决定是直接调用它还是调用它。该组件应该位于了解 Swing 线程的代码部分。这个组件应该只知道文件等。

于 2009-09-30T12:36:13.670 回答
1

我只与 jMock 合作了两天......所以如果有更优雅的解决方案,请原谅。:)

看起来您的 FileTableModel 取决于 SwingUtilities...您是否考虑过模拟您使用的 SwingUtilities?一种闻起来像 hack 但可以解决问题的方法是创建一个接口,比如 ISwingUtilities,并实现一个虚拟类 MySwingUtilities,它只是转发到真正的 SwingUtilities。然后在您的测试用例中,您可以模拟接口并为 isEventDispatchThread 返回 true。

@Test
public void testEventsNow() throws IOException {
    IFile testDir = mockDirectoryStructure();

    final ISwingUtilities swingUtils = context.mock( ISwingUtilities.class );

    final FileSystemEventsListener listener = 
                context.mock(FileSystemEventsListener.class);

    context.checking(new Expectations()
    {{
        oneOf( swingUtils ).isEventDispatchThread();
            will( returnValue( true ) );

        oneOf(listener).currentDirectoryChanged(with(any(IFile.class)));
    }});

    FileTableModel model = new FileTableModel(testDir);
    model.setSwingUtilities( swingUtils ); // or use constructor injection if you prefer
    model.switchToInnerDirectory(1);
}
于 2009-09-30T04:55:04.997 回答