2016-04-11 5 views
1

Ich lese this answer über Unit-Test eine Wrapper-Methode. Im folgenden Code:Unit-Test-Wrapper-Methoden, wenn Basis-Methode ist privat

public static Object methodA(Object arg) { 
    if (arg == null) { 
     return null; 
    } else { 
     return methodB(); 
    } 
} 

Es sieht vernünftig, dass ich brauche, um alle Funktionen nicht zu testen in methodB(), nur Test 2 Fällen, in denen arg null ist und nicht, weil methodB() sollte bereits getestet werden.

Wenn jedoch methodB()Privat Methode ist, sollte ich alle Funktionen testen, die methodB() bieten wird, da nach another answer, private Methoden Implementierungsdetails sind.

Das Problem ist, wenn ich zwei Methoden methodA1() und methodA2() und sie alle Anruf methodB() wie folgt aus:

public static MyObject methodA1(int x) { 
    MyObject obj = new MyObject(); 
    obj.setX(x); 
    return methodB(obj); 
} 

public static MyObject methodA2(int y) { 
    MyObject obj = new MyObject(); 
    obj.setY(y); 
    return methodB(obj); 
} 

private static MyObject methodB(MyObject obj) { 
    // doSomething with obj 
    return obj; 
} 

Sollte ich testen methodA1() und methodA2() getrennt? Oder kann ich nur testen Sie die private Methode methodB() weil methodA1() und methodA2() sind nur Wrapper-Methoden von methodB(), also wenn methodB() richtig getestet wird, werde ich nicht methodA1() und methodA2() überhaupt testen müssen.

EDIT:

Ich schrieb separate Tests für öffentliche Methoden auf den ersten. Aber das Problem ist, wenn es viele Varianten des methodA gibt und einige der Funktionalitäten/Anforderungen in einigen von ihnen geteilt werden, dann wird der Code der Testfälle dupliziert. Deshalb frage ich mich, ob ich private Methode methodB() testen sollte.

Das eigentliche Problem, das ich traf, ist ich habe Anforderung, einen Datensatz in der Datenbank hinzuzufügen, und es gibt viele verschiedene APIs, die ich bereitstellen sollte. Zum Beispiel sollte ich bieten:

MyObject addMale(String name, String job);  // fill sex with "Male" 
MyObject addStudent(String name, String sex); // fill job with "Student" 

Und alle von ihnen überprüft der Parameter gültig ist oder nicht, füllen Sie das Feld, das nicht angegeben wird und die private Methode aufrufen, tatsächlich um den Datensatz in die Datenbank einfügen, die so ist genannt methodB().

Es gibt viele APIs wie diese, wenn ich nur methodB() mit allen Feldern Situation testen kann, kann ich vielleicht die Vervielfältigung der Testfälle reduzieren, aber ist das eine gute Praxis, die Unit-Tests so zu machen?

EDIT2:

Ich weiß, dass ich private Methoden testen, weil ich Reflexion in meinen anderen Testfällen, und ich weiß es private Methoden aufrufen kann, auch. Aber meine Frage ist in dieser Situation, Testen privater Methoden ist eine richtige Lösung oder nicht.

+0

Wenn 'methodA1' etwas anderes als' methodA2' tut, dann benötigt es eindeutig einen separaten Test. Wenn beide Methoden dasselbe tun, benötigen Sie nur die eine Methode. – AJNeufeld

+0

Erweiterung eines Bits: 'methodA1' ruft **' setX' ** für ein Objekt auf und übergibt es an 'methodB', wobei' methodA2' ** 'setY' ** für ein Objekt aufruft und an 'methodB' übergibt . Das Verhalten von 'methodB' wird unterschiedlich sein, abhängig davon, ob das' X' des Objekts gesetzt ist oder ob sein 'Y' gesetzt ist. Beide müssen getestet werden. Wenn Sie 'methodB' getestet haben, müssten Sie sowieso Testfälle für beide Fälle schreiben. Macht es auch klar, indem Sie den Testfall für "A1" und "A2" schreiben. – AJNeufeld

+0

Das Ausführen einer Code-Coverage-Analyse Ihres Komponententests würde eine größere Abdeckung anzeigen, wenn Sie Komponententests für "methodA1" und "methodA2" statt nur für "methodB" ausführen würden. – AJNeufeld

Antwort

1

Das ist eigentlich eine Frage, über die ich mich schon eine Weile gewundert habe, und obwohl es nicht unbedingt korrekt ist, werde ich meine Meinung teilen.

Das Hauptproblem ist, dass private Methoden schwer zu testen sind. Ich habe ein wenig gegoogelt und dort einige Tools und Techniken, die dir Zugang zu privaten Methoden ermöglichen (Reflektion ist die Hauptsache, der ich begegnete), aber sie schienen ein wenig verworren.

Der Grund, warum Sie eine private Methode geschrieben haben, ist, dass eine andere Methode, eine öffentliche Methode, diese aufruft. Während Sie die private Methode nicht direkt testen können, können Sie ihre Funktionalität überprüfen, indem Sie die öffentliche Methode testen, die sie aufruft.

Wenn ich Sie wäre, würde ich ausgiebig testen methodA1() und methodA2(), um sicherzustellen, dass die gesamte Funktionalität von methodB() durch die Tests getestet wird man auf den öffentlichen Methoden ausgeführt werden.

+0

Wenn 'methodB()' eine Funktionalität hat, die zwischen 'methodA1()' und 'methodA2()' geteilt wird, wo soll ich es testen? Wenn ich es in beiden Tests teste, werden meine Testfälle dupliziert, und wenn ich es in einem von ihnen teste, wird es schwierig zu warten, weil ein solcher Test in jedem der Testfälle auftreten kann. – Nier

+1

Imho, du solltest 'methodA1() 'und' methodA2() '* complete * testen (was bedeutet, dass bei jedem Input, der wahrscheinlich da hineingeht - das" wahrscheinlich "von der Methode und der Verwendung abhängt, wenn es rein intern ist) es könnte weniger sein, als wenn es eine öffentliche API ist). Testen von nur 50% von 'methodA2() '(weil die anderen 50% die gleichen wie in' methodA1() 'sind) * kann * fatal sein - oder können Sie sicherstellen, dass sich in Ihrem Code nichts ändert? Sobald sich 'methodA2()' ändert, ist es vielleicht 50% ungeprüft - und fehlerhaft. Wenn Sie separate Komponententests durchführen, stellen diese zumindest sicher, dass sie weiterhin wie zuvor funktionieren. –

+0

@FlorianSchaetz Danke für die Meinung. Hast du den letzten Absatz gesehen, den ich bei der letzten Bearbeitung hinzugefügt habe? Was ist, wenn ich mehrere Varianten von 'methodA()' habe und sie eine gemeinsame Basis 'methodB()' teilen? Ich habe viele doppelte Fälle geschrieben, zum Beispiel: Name ist leer, Name ist nicht gültig, Name ist Null, Auftrag ist leer ... usw., und dieser duplizierte Code breitete sich in viele 'methodA' Testfälle aus. Wenn ich sie in 'methodB()' testen kann, kann ich den duplizierten Code in Testfällen reduzieren. In dieser Situation wird Unit-Test eine private 'MethodeB()' immer noch als eine falsche Lösung betrachtet? – Nier

1

Sie haben ein paar Meinungen erhalten, warum Sie Komponententests für methodA1() und methodA2() schreiben sollten; Du hast sie zurückgeschoben. Du hast die Frage hier gestellt. Sie suchen eindeutig nach einer Begründung dafür, dass Sie keinen vollständigen Komponententest für sie geschrieben haben. Mal sehen, ob wir Ihnen die Antwort geben können, die Sie wollen. ;-)

Von dem, was Sie in Ihrer Bearbeitung hinzugefügt haben, sieht es so aus, als hätten Sie eine Variation eines Builder-Musters. ZB)

MyObject o = new Builder().setX(1).setY(2).setZ(3).setW(4).build(); 

Wie würden wir den Builder testen?

Da der Builder 4 Attribute hat, die in beliebiger Reihenfolge gesetzt werden könnten, gäbe es 4! = 24 verschiedene Bestellungen. Eine komplette Testsuite müsste folgendes beinhalten:

@Test public void testXYZW() { ... } 
@Test public void testXYWZ() { ... } 
// ... 21 more permutations ... 
@Test public void testWZYX() { ... } 

Aber ist das alles? Nein! Einige dieser Attribute könnten Standardwerte haben, sodass wir diese Muster ebenfalls testen müssten. Die Gesamtordnungen sind nun P (4,4) + P (4,3) + P (4,2) + P (4,1) + P (4,0) = 24 + 24 + 12 + 4 + 1 = 85 Gerätetest.

@Test public void testXWY() { ... } 
@Test public void testWY() { ... } 
@Test public void testZ() { ... } 
// ... 61 more permutations ordering 

Und diese Tests nur jede Permutation von X, Y, Z, W-Attribute mit einem Testwert pro Attribut pro Test. Es ist eindeutig unhandlich, eine erschöpfende Reihe von Tests für jede mögliche Kombination und Permutation zu schreiben.

Der Designer der Builder-Klasse würde verstehen, dass die Permutation der Attributeinstellungen die resultierende Konstruktion nicht beeinflusst; Schreiben von Tests für Permutationen der Reihenfolge erhöht nicht tatsächlich die Testabdeckung. Tests für ausgelassene Attribute sind nützlich, da sie die Standardwerte testen. Das erneute Testen von verschiedenen Kombinationen der ausgelassenen Attribute würde die Testabdeckung nicht erhöhen. Also, nach sorgfältiger Überlegung, nur zwei Prüfungen erforderlich sein könnten:

@Test 
public void testXYZW() { 
    MyObject o = new Builder().setX(1).setY(2).setZ(3).setW(4).build(); 
    assertThat(o.wasBuiltProperly()); 
} 

@Test void testDefaults() { 
    MyObject o = new Builder().build(); 
    assertThat(o.wasBuiltProperlyFromDefaults()); 
} 

Wenn korrekte, vollständige Prüfung von methodB() getan wird, dann könnte man sicher weg mit Tests nur die Validierung von Eingaben in methodA1() und methodA2().

@Test void testMethodA1Professor() { 
    MyObject o = methodA1("professor"); 
    assertThat(o.wasBuiltProperlyWithProfessor()); 
} 

@Test void testMethodA1WithNull() { 
    MyObject o = methodA1(null); 
    assertThat(o.wasBuiltProperlyWithNull()); 
} 
0

Oft, wenn eine private Methode von mehreren Methoden innerhalb einer Klasse verwendet wird, lauert dahinter ein eindeutiges Konzept. Es kann eine eigene Klasse verdienen.

es nicht

außerhalb der Klasse verwendet werden soll

Es gibt ein paar Möglichkeiten, wie Sie methodB zu seiner eigenen Klasse extrahieren könnte und es nicht als Teil der öffentlichen API zur Verfügung stellen:

  • Nest ihm die Klasse im Innern, das es verwendet und gibt ihm einen eingeschränkten Anwendungsbereich

  • es zu einem lo in einem anderen Modul Put wer Ebene. Stellen Sie dieses Modul über Ihre API zur Verfügung, nicht jedoch über die Clients.

+0

Können Sie detailliertere Informationen darüber geben, warum 'methodB' eine eigene Klasse verdient? Da 'methodB' gut funktioniert, wenn es nur eine private Methode ist, kann ich nicht herausfinden, welchen Vorteil es hat, es in eine andere Klasse zu verschieben, was nur die Lesbarkeit verringert. – Nier

+0

Beachten Sie die Wörter * oft * und * können * in meiner Antwort. Ich bestätige nicht, dass es eine eigene Klasse verdient. Nur du hast das Wissen zu erzählen. – guillaume31

+0

Es geht nicht darum, gut zu arbeiten oder nicht gut zu funktionieren, es ist eine Frage der Kohäsion in der Klasse. Das [Prinzip der einheitlichen Verantwortung] (https://en.wikipedia.org/wiki/Single_responsibility_principle) sagt uns, dass, wenn Ihre Klasse zwei Gründe hat, sich zu ändern, weil 'methodB' unabhängig genug vom Rest ist, dann sollte es in zwei Teile geteilt werden . 1 Klasse = 1 Konzept. 2 Konzepte = 2 Klassen. – guillaume31