views:

278

answers:

5

I am writing a TotalCommander-like application. I have a separate component for file list, and a model for it. Model support listeners and issues a notification for events like CurrentDirChanged etc. in following manner:

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

I've written a simple test for this:

@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);
}

This does not work, because there is no EventDispatchThread. Is there any way to unit test this inside the headless build?

unit-testing java swing jmock

+1  A: 

Note, generally speaking unit testing on UI stuff is always difficult because you have to mock out a lot of stuff which is just not available.
Therefore the main aim when developing applications (of any type) is always to try to separate UI stuff from the main application logic as much as possible. Having strong dependencies here, make unit testing really hard, a nightmare basically. This is usually leveraged by using patterns like a MVC kind of approach, where you mainly test your controller classes and your view classes do nothing than constructing the UI and delegating their actions and events to the controllers. This separates responsibilities and makes testing easier.

Moreover you shouldn't necessarily test things which are provided by the framework already such as testing whether events are correctly fired. You should just test the logic you're writing by yourself.

Juri
I wrote this co and I want to test that it fires events when it should and with correct parameters. I guess, what I am doing wrong here, is to ensure GUI thread inside a model. Model is not a Swing component, it does not have to fire events inside a GUI thread. Am I thinking correctly here?
Ula Krukar
+5  A: 

Look this http://code.google.com/p/fest/

Looks interesting.
Ula Krukar
Sorry not to accept your answer, but I don't really want to test GUI, I only want to test my model without problems.
Ula Krukar
A: 

Check the uispec4j project. That's what I use to test my UIs.

www.uispec4j.org

Pigelvy
A: 

I've only been working with jMock for two days... so please excuse me if there is a more elegant solution. :)

It seems like your FileTableModel depends on SwingUtilities... have you considered mocking the SwingUtilities that you use? One way that smells like a hack but would solve the problem would be to create an interface, say ISwingUtilities, and implement a dummy class MySwingUtilities that simply forwards to the real SwingUtilities. And then in your test case you can mock up the interface and return true for isEventDispatchThread.

@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);
}
Philip Davis
A: 

I think the problem with testing is revealing a problem with the code. It shouldn't really be the model's job to decide whether it's running in the dispatch thread, that's too many responsibilities. It should just do its notification job and let a calling component decide whether to call it directly or to invokeLater. That component should be in the part of the code that knows about Swing threads. This component should only know about files and such.

Steve Freeman