简体   繁体   中英

Unit testing a Swing component

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

Look this :

FEST is a collection of libraries, released under the Apache 2.0 license , whose mission is to simplify software testing. It is composed of various modules, which can be used with TestNG or JUnit ...

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.

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

www.uispec4j.org

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM