2010-06-08 15 views
14

So habe ich für das folgende Stück Code, um das Layout Unit-Tests gestartet:Einheit Test Architektur Frage

public interface MyInterface { 
    void MyInterfaceMethod1(); 
    void MyInterfaceMethod2(); 
} 

public class MyImplementation1 implements MyInterface { 
    void MyInterfaceMethod1() { 
    // do something 
    } 

    void MyInterfaceMethod2() { 
    // do something else 
    } 

    void SubRoutineP() { 
    // other functionality specific to this implementation 
    } 
} 

public class MyImplementation2 implements MyInterface { 
    void MyInterfaceMethod1() { 
    // do a 3rd thing 
    } 

    void MyInterfaceMethod2() { 
    // do something completely different 
    } 

    void SubRoutineQ() { 
    // other functionality specific to this implementation 
    } 
} 

mit mehreren Implementierungen und die Erwartung, mehr zu kommen.

Mein erster Gedanke war mir Zeit Umschreiben Unit-Tests mit so etwas zu sparen:

public abstract class MyInterfaceTester { 
    protected MyInterface m_object; 

    @Setup 
    public void setUp() { 
    m_object = getTestedImplementation(); 
    } 

    public abstract MyInterface getTestedImplementation(); 

    @Test 
    public void testMyInterfaceMethod1() { 
    // use m_object to run tests 
    } 

    @Test 
    public void testMyInterfaceMethod2() { 
    // use m_object to run tests 
    } 
} 

die ich dann einfach die Implementierung spezifische zusätzliche Methoden wie so zu testen, Unterklasse könnte:

public class MyImplementation1Tester extends MyInterfaceTester { 
    public MyInterface getTestedImplementation() { 
    return new MyImplementation1(); 
    } 

    @Test 
    public void testSubRoutineP() { 
    // use m_object to run tests 
    } 
} 

und ebenfalls zur Ergänzung 2 weiter.

Also meine Frage ist wirklich: Gibt es einen Grund, dies nicht zu tun? JUnit scheint es gut zu mögen, und es erfüllt meine Bedürfnisse, aber ich habe in keinem der Unit-Testbücher und -beispiele, die ich gelesen habe, so etwas gesehen.

Gibt es einige Best Practice, die ich unwissentlich verletze? Richte ich mich für den Herzschmerz auf der Straße ein? Gibt es da einfach einen wesentlich besseren Weg, den ich nicht bedacht habe?

Danke für jede Hilfe.

Antwort

17

Gibt es einen Grund, dies nicht zu tun?

Nein. Tun Sie es. Tests sind Klassen für genau dieser Grund.

Ich habe so etwas in keinem der Unit Test Bücher und Beispiele, die ich gelesen habe.

Lesen Sie weiter. Einführungen decken dies nicht ab.

Gibt es einige Best Practices, die ich unwissentlich verletze?

Nr

Bin ich mich Einstellung auf der Straße für Herzeleid up?

Nr

Einige Leute werden nervös über "spröde Tests". Sie können hier einige Fragen finden, die nach Möglichkeiten suchen, dies so zu machen, dass eine Änderung der Software nicht zu Änderungen an den Tests führt. Auf lange Sicht ist der Versuch, "robuste" Tests zu erstellen, albern. Sie möchten Tests geschrieben, so dass jede kleine Änderung an der sichtbaren, Schnittstelle der Software Test Neuschreiben erfordert.

Sie möchten Tests, damit unsichtbare interne Änderungen kein Neuschreiben von Tests erfordern.

Die Verwendung von Klassen und Unterklassen ist orthogonal zu diesen Überlegungen.

Gibt es einfach einen viel besseren Weg da draußen, den ich nicht berücksichtigt habe?

Nein Objektausrichtung ist der Punkt. Tests sind genau aus diesem Grund eine Klasse.

5

Während ich SLott 100% unterstützen würde ich auch JUnit betrachten parametrisiert Tests statt Testklassenhierarchie für diese:

@RunWith(Parameterized.class) 
public class MyInterfaceTester { 
    private MyInterface m_object; 

    public void MyInterfaceTester(MyInterface object) { 
    m_object = object; 
    } 

    @Parameters 
    public static Collection<Object[]> data() { 
    List<Object[]> list = new ArrayList<Object[]>(); 

    list.add(new Object[]{new MyImplementation1()}); 
    list.add(new Object[]{new MyImplementation2()}); 

    return list; 
    } 

    @Test 
    public void testMyInterfaceMethod1() { 
    // use m_object to run tests 
    } 

    @Test 
    public void testMyInterfaceMethod2() { 
    // use m_object to run tests 
    } 
} 

Keine Notwendigkeit, in der Hierarchie Testklasse: nur neue Implementierung hinzufügen, indem Sie eine weitere Liste Element hinzugefügt wird in data Methode.

+0

Ich wusste nichts von dieser Funktion, vielen Dank für die Freigabe. Weitere Informationen und ein funktionierendes Beispiel finden Sie hier: http://www.devx.com/Java/Article/31983/0/page/3 für alle anderen Interessierten. –

1

Wenn Sie in jeder Testklasse wirklich das gleiche Setup machen und abreißen, dann ist das, was Sie tun, in Ordnung, aber ich finde, dass dies in der Praxis fast nie der Fall ist. In der Tat, die meiste Zeit mit einer Setup-Methode, die Testdaten instanziiert, wie Sie hier haben, ist nicht einmal das, was Sie wollen. Stattdessen richten Sie innerhalb einer Testklasse eine beliebige Infrastruktur ein und jede Testmethode richtet ihre eigene Instanz eines Objekts ein, um einige Aspekte davon zu testen.

+0

Wahr, den getTestedImplementation() - Aufruf in die erste Zeile jedes Tests zu verschieben und das Ergebnis in eine lokale Position zu bringen, könnte das sein, was ich in der Zeile haben möchte. Wahrscheinlich sicherer, mit einem neuen Objekt zu beginnen. Derzeit gibt es jedoch keinen Status in den Implementierungsklassen, daher sollten sie nur einmal erstellt werden. –