2009-09-26 28 views
7

Piszę aplikację podobną do TotalCommander. Mam osobny komponent dla listy plików i jego model. słuchacze wsparcia modelowe i problemy powiadomienie o zdarzeniach takich jak CurrentDirChanged itp w następujący sposób:Jednostka testująca komponent 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); 
      } 
     }); 
    } 
} 

Napisałem prosty test na to:

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

To nie działa, ponieważ nie ma to EventDispatchThread. Czy istnieje jakiś sposób, aby przetestować to urządzenie wewnątrz bezgłowej konstrukcji?

jednostka testowanie Java Swing JMock

Odpowiedz

10

Uwaga, mówiąc ogólnie testów jednostkowych na UI rzeczy jest zawsze trudne, ponieważ trzeba wyśmiewać się wiele rzeczy, które po prostu nie jest dostępny.
Dlatego głównym celem podczas opracowywania aplikacji (dowolnego typu) jest zawsze jak największa separacja elementów interfejsu użytkownika od głównej logiki aplikacji. Posiadanie silnych zależności sprawia, że ​​testowanie jednostek jest naprawdę trudne, w zasadzie koszmar. Zazwyczaj wykorzystuje się takie wzorce, jak np. Podejście typu MVC, w którym głównie testowane są klasy kontrolerów, a klasy widokowe nic nie robią, niż konstruowanie interfejsu użytkownika i delegowanie ich akcji i zdarzeń do kontrolerów. To oddziela odpowiedzialność i ułatwia testowanie.

Co więcej, nie powinieneś testować rzeczy dostarczonych przez framework, takich jak testowanie, czy zdarzenia są prawidłowo uruchamiane. Powinieneś po prostu przetestować logikę, którą piszesz sam.

+1

Napisałem tę aplikację i chcę przetestować, czy uruchamia ona zdarzenia, kiedy powinna i przy prawidłowych parametrach.Myślę, że to, co robię tutaj źle, to zapewnienie wątku GUI w modelu. Model nie jest składnikiem Swing, nie musi wywoływać zdarzeń wewnątrz wątku GUI. Czy właściwie tu myślę? –

12

Look this:

FEST to zbiór bibliotek, wydany pod Apache 2.0 license, którego zadaniem jest uproszczenie testowania oprogramowania. Składa się z różnych modułów, które mogą być używane z TestNG lub JUnit ...

+0

Wygląda interesująco. –

+0

Niestety, nie akceptuję twojej odpowiedzi, ale tak naprawdę nie chcę testować GUI, chcę tylko przetestować mój model bez problemów. –

2

Sprawdź projekt uispec4j. Właśnie tego używam do testowania moich interfejsów.

www.uispec4j.org

+0

wygląda jak ....... nieważny projekt? (link nie wydaje się już iść tam, gdzie było wcześniej) – Snappawapa

1

ja tylko pracuję z JMock przez dwa dni ... więc proszę mi wybaczyć, jeśli istnieje bardziej eleganckie rozwiązanie. :)

Wygląda na to, że Twój FileTableModel zależy od SwingUtilities ... czy rozważałeś kpiny z SwingUtilities, których używasz? Jednym ze sposobów, który pachnie jak włamanie, ale rozwiąże problem będzie stworzenie interfejsu, powiedzmy ISwingUtilities, i zaimplementowanie dummy klasy MySwingUtilities, które po prostu przekieruje do prawdziwych SwingUtilities. A następnie w twoim teście testowym możesz stworzyć interfejs i zwrócić wartość true dla 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

Jest to w zasadzie, jak to robimy. Zamieniamy SwingUtilities na OurSwingUtilities.getInstance(), a następnie w testach mamy alternatywną implementację (nie używamy jMock do tego, ponieważ wiele testów może wygodniej współdzielić pojedynczą klasę). Robimy to dla SwingWorker.execute() oraz wiele narzędzi, które są statyczne w bibliotece Java. – Trejkaz

2

Myślę, że problem z testowaniem ujawnia problem z kodem. Nie powinno tak naprawdę być zadaniem modelu, aby zdecydować, czy działa w wątku wysyłki, to zbyt wiele obowiązków. Powinien po prostu wykonać zadanie powiadamiania i pozwolić komponentowi wywołującemu zdecydować, czy wywołać to bezpośrednio, czy wywołać później. Ten komponent powinien znajdować się w części kodu, która zna wątki Swing. Ten komponent powinien wiedzieć tylko o plikach i takich.

Powiązane problemy