2010-07-28 8 views
15

In vielen Anwendungs ​​habe ich oft Algorithmen, die Verwendung von speziellen Sub-Algorithmen (oder einfach gut definierte Teile des Codes) zu machen.Wie teste ich lokale innere Klassenmethoden in Java?

Bis jetzt, wenn ich den Hauptalgorithmus schrieb, habe ich eine private Methode für jeden Teilalgorithmus wie im Beispiel unten (OldStyle):

public class OldStyle { 

    public int mainAlg() { 
     int x = subAlg01(); 
     int y = subAlg02(); 
     int z = x * y; 
     return z; 
    } 

    private int subAlg01() { 
     return 3; 
    } 

    private int subAlg02() { 
     return 5; 
    } 
} 

Das funktionierte gut, aber ich wusste nicht, wie mit eine Proliferation von Methoden (subAlg01 und subAlg02), die, wenn auch privat, nur von einer Methode (mainAlg) verwendet wurden.

Kürzlich dicovered ich die Verwendung von lokalen inneren Klassen und jetzt ist mein Beispiel ist (NewStyle):

public class NewStyle { 

    public int mainAlg() { 
     class Nested { 

      public int subAlg01() { 
       return 3; 
      } 

      public int subAlg02() { 
       return 5; 
      } 
     } 
     Nested n = new Nested(); 
     int x = n.subAlg01(); 
     int y = n.subAlg02(); 
     int z = x * y; 
     return z; 
    } 
} 

Ich mag es sehr viel, aber jetzt habe ich folgendes Problem: Wie teste ich subAlg01 und subAlg02 mit JUnit?

By the way: Ich bin mit Eclipse.

Danke für Ihre Hilfe.

Bearbeiten: Ich versuche, besser zu erklären: Ich habe, sagen wir, einen Sortieralgorithmus und ich möchte es testen, um sicherzustellen, dass es wie erwartet ausgeführt wird. Diese Sortieralgorithmen werden nur von der Methode m der Klasse X verwendet. Ich könnte sie zu einer privaten Methode der Klasse X machen, aber Klasse X hat normalerweise nichts mit Sortierung zu tun, also warum die Klasse X mit der Sortiermethode "verderben"? Also lege ich es in Methode m. Einige Zeit später möchte ich meinen Sortieralgorithmus verbessern (ich mache es schneller), aber ich möchte sicher sein, dass das Verhalten wie erwartet ist, also möchte ich es mit den ursprünglichen Tests erneut testen.

Das ist, was ich tun will, vielleicht ist es keine Lösung, ich hoffe, dass jemand mir helfen kann.

bearbeiten nach Antwort Wahl. Ich wählte die Antwort von Rodney aus, weil seine Lösung die ist, die ich adoptiert habe: Eine eigenständige Hilfsklasse hilft mir (es ist ein Helfer!), Eine klare Sicht auf die Submethoden zu haben und gibt mir auch die Möglichkeit, sie zu testen.

+0

I Das Eclipse-Tag wurde entfernt, da dieses Problem nicht spezifisch dafür ist. –

Antwort

10

Filippo, ich verstehe Ihre Frustration mit dem Problem und mit einigen der Antworten. Als ich vor vielen Jahren anfing, JUnit zu benutzen, wollte ich auch privaten Code testen, und ich hielt es für dumm von den Gurus zu sagen, dass es eine schlechte Idee war.

Es stellte sich heraus, dass sie richtig waren (Überraschung!), Aber erst nachdem ich einige Tests geschrieben hatte, verstand ich warum. Sie müssen möglicherweise den gleichen Prozess durchlaufen, aber Sie werden schließlich zur gleichen Schlussfolgerung kommen ;-)

Wie auch immer, in Ihrer Situation würde ich Nested in eine geeignete eigenständige Klasse, möglicherweise in einem separaten Paket, um offensichtlich zu machen dass es eine Hilfsklasse ist. Dann würde ich direkt dafür Tests schreiben, unabhängig von anderen Tests.

Dann würde ich Tests für NewStyle schreiben und nur auf das Verhalten von NewStyle konzentrieren.

(Sehr wahrscheinlich würde ich auch Nested in NewStyle eher injizieren als es innerhalb NewStyle Instanziieren -. Dh es ein Argument Konstruktor zu NewStyle‘machen

Dann, wenn ich Tests für NewStyle im Test schreiben würde ich Pass in einer Instanz von Nested und weitermachen. Wenn ich besonders heikel fühlte ich eine Schnittstelle aus Nested schaffen würde und eine zweite Implementierung erstellen und Test NewStyle mit das auch.)

+0

+1, weil Ihre Lösung die gleiche ist, die ich derzeit angenommen habe (eigenständige Helper-Klasse) –

16

Sie sollten nur die öffentliche Schnittstelle von Klassen testen, nicht private Mitglieder oder private innere Klassen. Private Mitglieder sind Implementierungsdetails, die nur von öffentlichen Methoden der Klasse (direkt oder indirekt) verwendet werden. Sie können diese also indirekt über ihre Aufrufmethoden testen. Wenn Sie das Gefühl haben, dass Sie in diesen Komponententests nicht genügend Granularität haben oder dass Sie die Ergebnisse, an denen Sie interessiert sind, nicht wahrnehmen können, deutet dies wahrscheinlich auf ein Problem mit Ihrem Design hin: Die Klasse könnte zu groß sein und versuchen zu viel zu tun, daher müssen einige seiner Funktionen möglicherweise in eine separate Klasse extrahiert werden, wo sie dann direkt in der Einheit getestet werden können.

Im aktuellen Beispiel, wenn die innere Klasse selbst eine Menge Code enthält, können Sie einfach schalten Sie ihn in eine Klasse der obersten Ebene, dann können Sie Gerät direkt seine Methoden testen.

(BTW Ihre innere Klasse sollte static sein, wenn es an die umgebenden Klasse Instanz bezieht sich nicht benötigen.)

+0

Hallo, danke für die Antwort. Ich habe schon viele Antworten gelesen wie Sie, aber mir ist die Bedeutung nicht ganz klar. Bitte können Sie mir helfen, besser zu verstehen? Ich habe ein Stück Code, das nur einmal verwendet wird, aber ich muss überprüfen, ob es tut, was es erwartet. Hab ich das geschafft? Tests schienen mir eine gute Wahl zu sein. –

+0

@Filippo, siehe mein Update. Testen ist in der Tat eine gute Wahl - schreiben Sie Tests, die alle möglichen Anwendungsszenarien abdecken. Ein Tool zur Codeabdeckung hilft Ihnen, nicht getestete Codeteile zu finden. –

+1

@ Péter über Ihren Vorschlag auf statische: innere Klassen können statische lokale innere Klasse Nr. –

1

Sie diese Klassen von außen nicht erreichen kann, so junit Them nicht testen können. Sie müssen Dinge öffentlich haben, um sie zu testen.

+0

Dies ist nicht korrekt. Mock-Frameworks wie JMockit können private innere Klassen (sowie private Methoden und Mitglieder) gut behandeln. – jchilders

+1

JMockit macht am ehesten ungezogene Sachen mit Reflektion, was junit nicht tut. –

+0

Wissen Sie, ob JMockit (oder ähnliche Frameworks) Methoden innerhalb lokaler innerer Klassen testen können? –

1

sollten Sie vermeiden, über Ihren Code zu verkomplizieren, nur weil Sie

weiß nicht, wie eine Proliferation von Methoden mit

In Ihrem Fall, dass Sie die Methoden testen können, wenn sie Methoden der ARE äußere Klasse oder wenn Sie die innere Klasse unbedingt benötigen, platzieren Sie sie außerhalb der Methode mainAlg(), sodass sie global sichtbar ist.

public class NewStyle { 

    public int mainAlg() { 

     Nested n = new Nested(); 
     int x = n.subAlg01(); 
     int y = n.subAlg02(); 

     int z = x * y; 
     return z; 
    } 

    class Nested { 

     public int subAlg01() { 
      return 3; 

     } 

     public int subAlg02() { 
      return 5; 
     } 
    } 
} 

dies kann dann new NewStyle().new Nested().subAlg01();

+0

Danke für die Antwort. Ich habe auch diese Lösung ausprobiert, aber ich mag sie weniger als meine, denn wenn ich mir die Klasse anschaue, habe ich immer noch keine klare Vorstellung, dass die subAlgs nur ein Teil des Hauptalgorithmus sind und von niemandem benutzt werden. Trotzdem erlaubt es Tests. –

+0

new NewStyle(). New Verschachtelt(). SubAlg01(); Bist du sicher? NIEMALS habe ich das gesehen .../ponder –

1

Hm mit heißen, ich weiß, dass Sprachen wie Groovy sind oft auf Java zu tun Unit-Tests verwendet und sind in der Lage transparent private Felder und Methoden für den Zugriff auf .... .aber ich bin nicht sicher über verschachtelte Klassen. Ich kann verstehen, warum Sie so etwas tun sollten, aber ich nehme die gleiche Position ein, die ich bei der Erprobung von Gettern und Setter einnehme, sie sollten als Nebeneffekt getestet werden, wenn Sie Ihre öffentlichen Methoden testen, und wenn nicht Auf diese Weise getestet werden, warum sind sie dann da?

1

Das Problem kommt, wenn Ihre innere Klassenverhalten ist ein zentraler Bestandteil von wh Bei einer Methode heißt das: Eine Methode soll ein runnable an ein drittes Objekt übergeben, das eine innere Klasse ist, die eigentlich keine separate Entität sein sollte: In diesem Fall erfordert ein richtiger Komponententest ziemlich viel Übung besagter innerer klasse, indem er dieses dritte objekt verspottet und argument capture benutzt, um es zu testen, was er sollte.

Diese Art von Problemen sind sehr häufig in Sprachen, die mehr Unterstützung für funktionale Programmierung haben, wo das Testen von Lambdas, die an Dritte weitergegeben werden, ziemlich eine Voraussetzung ist.

Aber wie schon gesagt, wenn Ihre innere Klasse nie von einer externen Partei benutzt wird, dann ist es nur ein Implementierungsdetail, und das separate Testen ist einfach nicht sinnvoll.

1

Eigentlich können Sie Ihre lokale innere Klasse testen. Aber es muss eine Schnittstelle implementieren. Auf diese Weise können Sie eine Instanz der lokalen Klasse über Reflektion erstellen und sie auf ihre Schnittstelle umwandeln. Sobald Sie den Schnittstellentyp haben, können Sie Ihre Implementierung Code ohne Probleme testen:

Die Schnittstelle:

public interface Algorithm { 
    int subAlg01(); 
    int subAlg02(); 
} 

Die Klasse:

public class NewStyle { 
    public int mainAlg() { 
     class Nested implements Algorithm { 

      public int subAlg01() { 
       return 3; 
      } 

      public int subAlg02() { 
       return 5; 
      } 
     } 
     Nested n = new Nested(); 
     int x = n.subAlg01(); 
     int y = n.subAlg02(); 
     int z = x * y; 
     return z; 
    } 
} 

Der Test:

public class NewStyleTest { 

    @Test 
    public void testLocal() throws ClassNotFoundException, 
      NoSuchMethodException, SecurityException, InstantiationException, 
      IllegalAccessException, IllegalArgumentException, 
      InvocationTargetException { 
     Class<?> forName = Class.forName("NewStyle$1Nested"); 
     Constructor<?> declaredConstructor = forName 
       .getDeclaredConstructor(NewStyle.class); 
     declaredConstructor.setAccessible(true); 
     Algorithm algorithm = (Algorithm) declaredConstructor 
       .newInstance(new NewStyle()); 

     assertEquals(algorithm.subAlg01(), 3); 
     assertEquals(algorithm.subAlg02(), 5); 
    } 
}