2009-09-26 12 views
7

Sto scrivendo un'applicazione simile a TotalCommander. Ho un componente separato per la lista dei file e un modello per questo. ascoltatori di supporto del modello e le questioni di una notifica per eventi come CurrentDirChanged ecc in modo seguente:Unit test su un componente 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); 
      } 
     }); 
    } 
} 

Ho scritto un semplice test per questo:

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

Questo non funziona, perché non c'è EventDispatchThread. C'è un modo per testare l'unità all'interno della configurazione senza testa?

unit test Java Swing jMock

risposta

10

nota, in generale unit testing su roba UI è sempre difficile perché si deve prendere in giro un sacco di roba che è solo non è disponibile.
Pertanto, l'obiettivo principale nello sviluppo di applicazioni (di qualsiasi tipo) è sempre quello di provare a separare il più possibile le interfacce utente dalla logica dell'applicazione principale. Avere forti dipendenze qui, rende i test unitari davvero difficili, un incubo in pratica. Solitamente questo viene sfruttato utilizzando modelli come un tipo di approccio MVC, in cui si testano principalmente le classi del controllore e le classi di visualizzazione non fanno altro che costruire l'interfaccia utente e delegare le loro azioni ed eventi ai controller. Questo separa le responsabilità e facilita i test.

Inoltre, non è necessario testare necessariamente le informazioni fornite dal framework, come ad esempio verificare se gli eventi sono stati correttamente attivati. Dovresti solo testare la logica che stai scrivendo da solo.

+1

Ho scritto questo co e voglio testare che spara gli eventi quando dovrebbe e con i parametri corretti.Immagino, quello che sto facendo male qui, è quello di garantire il thread GUI all'interno di un modello. Il modello non è un componente Swing, non deve generare eventi all'interno di un thread della GUI. Sto pensando correttamente qui? –

12

sguardo this:

FEST è una collezione di librerie, rilasciato sotto Apache 2.0 license, la cui missione è quella di semplificare il test del software. E 'composto da diversi moduli, che possono essere utilizzati con TestNG o JUnit ...

+0

Sembra interessante. –

+0

Mi spiace non accettare la tua risposta, ma in realtà non voglio testare la GUI, voglio solo testare il mio modello senza problemi. –

2

Controllare il progetto uispec4j. Questo è quello che uso per testare le mie interfacce utente.

www.uispec4j.org

+0

sembra un ... ... progetto defunto? (link non sembra più andare dove prima) – Snappawapa

1

Ho solo lavorato con jMock per due giorni ... quindi per favore mi scusi se c'è una soluzione più elegante. :)

Sembra che il tuo FileTableModel dipenda da SwingUtilities ... hai preso in considerazione il deridere con SwingUtilities che usi? Un modo che puzza di hack ma risolverebbe il problema sarebbe quello di creare un'interfaccia, ad esempio ISwingUtilities, e implementare una classe fittizia MySwingUtilities che semplicemente inoltra alle reali SwingUtilities. E poi nel tuo caso di test puoi simulare l'interfaccia e restituire true per 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); 
} 
+0

Questo è fondamentalmente come lo facciamo. Sostituiamo SwingUtilities con OurSwingUtilities.getInstance() e poi nei test, abbiamo un'implementazione alternativa (non usiamo jMock per questo, perché molti test possono condividere più facilmente una singola classe). Lo facciamo per SwingWorker.execute() così come un sacco di utility che sono statiche nella libreria Java. – Trejkaz

2

Penso che il problema con il test sta rivelando un problema con il codice. Non dovrebbe essere il lavoro del modello decidere se è in esecuzione nel thread di invio, sono troppe le responsabilità. Dovrebbe solo fare il suo lavoro di notifica e lasciare che un componente chiamante decida se chiamarlo direttamente o invocareLater. Quel componente dovrebbe essere nella parte del codice che conosce i thread Swing. Questo componente dovrebbe conoscere solo i file e così via.