I am looking for a method to perform a drag-and-drop of a file/multiple files onto my application from a unit test. For example selecting some files in Windows Explorer, drag them and drop them on my application.
I am capable of testing drag-and-drop behavior between two components in my application (see below - feel free to indicate if you know a better way), but I have no idea how to do the same when the data has to come from outside my application.
I thought about using the debugger to inspect a 'file' Transferable
when I do the drag-and-drop operation by hand, but there must be a better way then hard-coding a complete Transferable
.
Example of a drag-and-drop test between components
import org.junit.Test;
import javax.swing.Action;
import javax.swing.JTextField;
import javax.swing.TransferHandler;
import java.awt.event.ActionEvent;
import static org.junit.Assert.assertEquals;
public class DragAndDropTest {
@Test
public void dragAndDropBetweenTwoTextFields() {
JTextField firstField = new JTextField();
JTextField secondField = new JTextField();
String testText = "Test text";
firstField.setText( testText );
firstField.selectAll();
Action copyAction = TransferHandler.getCopyAction();
copyAction.actionPerformed( new ActionEvent( firstField, ActionEvent.ACTION_PERFORMED, "Copy" ) );
Action pasteAction = TransferHandler.getPasteAction();
pasteAction.actionPerformed( new ActionEvent( secondField, ActionEvent.ACTION_PERFORMED, "Paste" ) );
assertEquals( "Text is not copied", testText, secondField.getText() );
}
}
Edit
Based on the comments under this question, I have updated my code snippet to 'fake' a drag-and-drop by providing a hard-coded Transferable
. The code also contains a little main program which just creates an empty frame on which you can drop files. The path will then be printed on the console.
On my PC, drag-and-drop of a file does not use the javaFileListFlavor
but the URI flavor.
Previous experiences (see this question) already teached me that the Transferable
a component receives when dragging something from outside the Java application might differ in subtle ways.
So to be completely clear: I want to test the part of my code that extracts the information from the received Transferable
. The 'information-handling' code (e.g. what happens when the application receives the file) can easily be tested without bothering with D&D. I just need to make sure I extract the correct information from the Transferable
, and it is silly to test this with a hard-coded Transferable
.
import org.junit.Test;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.TransferHandler;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.util.List;
import static org.junit.Assert.assertEquals;
public class DragAndDropTest {
private static DataFlavor URI_LIST_FLAVOR = null;
static {
try {
URI_LIST_FLAVOR = new DataFlavor( "text/uri-list;class=java.lang.String" );
}
catch ( ClassNotFoundException ignore ) {
}
}
@Test
public void testFileDragAndDrop() throws IOException, UnsupportedFlavorException {
JComponent testComponent = new JPanel();
TestingTransferHandler transferHandler = new TestingTransferHandler();
testComponent.setTransferHandler( transferHandler );
Clipboard clipBoard = new JLabel( ).getToolkit().getSystemClipboard();
Transferable transferable = new Transferable() {
@Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[]{URI_LIST_FLAVOR};
}
@Override
public boolean isDataFlavorSupported( DataFlavor flavor ) {
return flavor == URI_LIST_FLAVOR;
}
@Override
public Object getTransferData( DataFlavor flavor ) throws UnsupportedFlavorException, IOException {
if ( flavor == URI_LIST_FLAVOR ) {
return new String( "file:///home/robins/Desktop/swingx-1.0-javadoc.jar" );
}
throw new UnsupportedFlavorException( flavor );
}
};
clipBoard.setContents( transferable, null );
Action pasteAction = TransferHandler.getPasteAction();
pasteAction.actionPerformed( new ActionEvent( testComponent, ActionEvent.ACTION_PERFORMED, "Paste" ) );
assertEquals( transferable.getTransferData( URI_LIST_FLAVOR ), transferHandler.transferable.getTransferData( URI_LIST_FLAVOR ) );
}
private static class TestingTransferHandler extends TransferHandler{
public Transferable transferable;
@Override
public boolean canImport( TransferSupport support ) {
return true;
}
@Override
public boolean importData( TransferSupport support ) {
transferable = support.getTransferable();
try{
if ( transferable.isDataFlavorSupported( DataFlavor.javaFileListFlavor ) ) {
System.out.println("File list flavor");
List<File> fileList = ( List<File> ) transferable.getTransferData( DataFlavor.javaFileListFlavor );
System.out.println( "fileList = " + fileList );
}
if ( transferable.isDataFlavorSupported( URI_LIST_FLAVOR )){
System.out.println("URI list flavor");
String uriList = ( String ) transferable.getTransferData( URI_LIST_FLAVOR );
System.out.println( "uriList = " + uriList );
}
return true;
} catch ( UnsupportedFlavorException e ) {
return false;
} catch ( IOException e ) {
return false;
}
}
}
public static void main( String[] args ) {
EventQueue.invokeLater( new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame( "TestFrame" );
JPanel contentPane = new JPanel( new BorderLayout( ) );
contentPane.setTransferHandler( new TestingTransferHandler() );
frame.setContentPane( contentPane );
frame.setSize( 200,200 );
frame.setVisible( true );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
}
} );
}
}
Unit-testing Swing GUI is always painful, let alone D&D. However, I believe it can be done.
There are two things to notice:
Currently, you're not testing D&D at all, but rather copy-paste. This is also a form of data transfer, but the use of copyAction
and pasteAction
causes you to really not go through any of the steps of D&D
You mention that you want to check the part that receives the file, and therefore you created a custom Transferable, but I would like to argue that you're trying to test the wrong thing. The part that handles the data can be implemented as a package private method handling a Transferable
containing the data, and tested with a simple unit test (no Swing components involved)
I believe that what you should be testing, if you care about D&D differences between different OS, is that the D&D process itself is working, i.e.:
DragSource
) is requested to provide the data, and that it provides itDropTarget
) receives the data and handles itYou can test this by either using FEST
's ComponentDragAndDrop
class (see here).
If you want to write you own implementation (why would you want to do that ?!) you could:
Replace both the source and target components' TransferHandler
s with mocks (or spies to be more accurate), that on top of calling the real methods, would enable you to verify that the expected methods are called with the expected data
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With