2009-09-26 92 views
7

我在写一个类似TotalCommander的应用程序。我有一个单独的文件列表组件和一个模型。型号支持的听众和问题,在下面的方式像CurrentDirChanged等事件的通知:单元测试一个Swing组件

 
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的

回答

10

注意,在UI的东西一般来说单元测试是很困难的,因为你必须模拟出了很多的东西,这仅仅是不可用的。
因此,开发应用程序(任何类型)时的主要目标总是尽可能地将UI主体与主应用程序逻辑分开。在这里有很强的依赖性,使得单元测试真的很难,基本上是一场噩梦。通常通过使用诸如MVC类型的方法来利用这种方式,其中您主要测试您的控制器类,并且您的视图类除了构建UI并将其操作和事件委托给控制器之外没有任何作用。这将职责分开并使测试更容易。

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

+1

我写了这篇文章,并且我想测试它在应该使用正确的参数时触发事件。我猜,我在这里做错了什么,是确保模型中的GUI线程。模型不是Swing组件,它不必在GUI线程内触发事件。我在这里想的是对的吗? – 2009-09-26 10:34:24

12

this

巨星是库的集合,下Apache 2.0 license发布的,其使命是简化软件测试。它是由各种模块,可与TestNGJUnit使用的...

+0

看起来很有趣。 – 2009-09-26 12:35:54

+0

对不起,不接受你的答案,但我不想测试GUI,我只想测试我的模型没有问题。 – 2009-09-27 12:59:35

2

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

www.uispec4j.org

+0

看起来像一个......失效的项目? (链接似乎不再去往哪里) – Snappawapa 2018-01-26 01:58:26

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); 
} 
+0

这基本上就是我们如何做到的。我们将SwingUtilities替换为OurSwingUtilities.getInstance(),然后在测试中,我们有一个替代实现(我们不使用jMock,因为很多测试可以更方便地共享一个类。)我们为SwingWorker.execute ()以及Java库中静态的大量实用程序。 – Trejkaz 2013-01-30 03:54:59

2

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