单元测试Swing组件

时间:2009-09-26 09:28:01

标签: java unit-testing swing jmock

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

private void fireCurrentDirectoryChanged(final IFile dir) {
    if (SwingUtilities.isEventDispatchThread())
        for (FileTableEventsListener listener : tableListeners)
            listener.currentDirectoryChanged(dir);
    else {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                for (FileTableEventsListener listener : tableListeners)
                    listener.currentDirectoryChanged(dir);
            }
        });
    }
}

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

@Test
public void testEvents() throws IOException {
    IFile testDir = mockDirectoryStructure();
    final FileSystemEventsListener listener = 
                context.mock(FileSystemEventsListener.class);
    context.checking(new Expectations() {{
        oneOf(listener).currentDirectoryChanged(with(any(IFile.class)));
    }});

    FileTableModel model = new FileTableModel(testDir);
    model.switchToInnerDirectory(1);
}

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

单元测试java swing jmock

5 个答案:

答案 0 :(得分:12)

this

  

FEST是一系列库,在Apache 2.0 license下发布,其任务是简化软件测试。它由各种模块组成,可与TestNGJUnit ...

一起使用

答案 1 :(得分:11)

注意,一般来说,对UI内容进行单元测试总是很困难,因为你必须嘲笑很多不可用的东西。
因此,开发应用程序(任何类型)时的主要目的始终是尝试尽可能地将UI内容与主应用程序逻辑分开。在这里拥有强大的依赖性,使单元测试变得非常困难,基本上是一场噩梦。这通常通过使用类似MVC方法的模式来实现,其中您主要测试控制器类,而视图类除了构建UI并将其操作和事件委派给控制器之外什么都不做。这将责任分开,使测试更容易。

此外,您不一定要测试框架提供的内容,例如测试事件是否被正确触发。你应该自己测试你正在编写的逻辑。

答案 2 :(得分:2)

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

www.uispec4j.org

答案 3 :(得分:2)

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

答案 4 :(得分:1)

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

看起来你的FileTableModel依赖于SwingUtilities ......你考虑过嘲笑你使用的SwingUtilities吗?一种闻起来像黑客但会解决问题的方法是创建一个接口,比如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);
}