Sie haben hier einen Konflikt.
Sie möchten den Rückgabewert von testen doThings()
, der auf einem Literal (const-Wert) basiert.
Jeder Test, den Sie dafür schreiben, führt dazu, dass ein konstanter Wert getestet wird , was unsinnig ist.
Um Ihnen ein sinnlicheres Beispiel zu zeigen (ich bin schneller mit C #, aber das Prinzip ist das gleiche)
public class TriplesYourInput : Base
{
public TriplesYourInput(int input)
{
this.foo = 3 * input;
}
}
Diese Klasse kann sinnvoll getestet werden:
var inputValue = 123;
var expectedOutputValue = inputValue * 3;
var receivedOutputValue = new TriplesYourInput(inputValue).doThings();
Assert.AreEqual(receivedOutputValue, expectedOutputValue);
Das Testen ist sinnvoller. Sein Ausgang ist auf den Eingang aus , dass Sie gewählt haben , es zu geben. In einem solchen Fall können Sie einer Klasse eine willkürlich ausgewählte Eingabe geben, ihre Ausgabe beobachten und testen, ob sie Ihren Erwartungen entspricht.
Einige Beispiele für dieses Testprinzip. Beachten Sie, dass meine Beispiele immer die direkte Kontrolle über die Eingabe der testbaren Methode haben.
- Testen Sie, ob
GetFirstLetterOfString()
"F" zurückgegeben wird, wenn ich "Flater" eingebe.
- Testen Sie, ob
CountLettersInString()
bei Eingabe von "Flater" 6 zurückgegeben wird.
- Testen Sie, ob
ParseStringThatBeginsWithAnA()
bei der Eingabe von "Flater" eine Ausnahme zurückgegeben wird.
Alle diese Tests können jeden gewünschten Wert eingeben , solange ihre Erwartungen mit den eingegebenen übereinstimmen .
Wenn Ihre Ausgabe jedoch durch einen konstanten Wert bestimmt wird, müssen Sie eine konstante Erwartung erstellen und dann testen, ob die erste mit der zweiten übereinstimmt. Was albern ist, das wird entweder immer oder nie passieren; Beides ist kein aussagekräftiges Ergebnis.
Einige Beispiele für dieses Testprinzip. Beachten Sie, dass diese Beispiele keine Kontrolle über mindestens einen der zu vergleichenden Werte haben.
- Testen Sie, ob
Math.Pi == 3.1415...
- Testen Sie, ob
MyApplication.ThisConstValue == 123
Diese Tests für einen bestimmten Wert. Wenn Sie diesen Wert ändern, schlagen Ihre Tests fehl. Im Wesentlichen testen Sie nicht, ob Ihre Logik für eine gültige Eingabe funktioniert, sondern nur, ob jemand in der Lage ist , ein Ergebnis genau vorherzusagen , über das er keine Kontrolle hat.
Das testet im Wesentlichen das Wissen des Testautors über die Geschäftslogik. Es wird nicht der Code getestet, sondern der Autor selbst.
Zurück zu Ihrem Beispiel:
class BarDerived : public Base
{
public:
BarDerived() : Base(12) { };
~BarDerived() { };
int doBarThings() { return foo + 1; };
}
Warum hat BarDerived
immer ein foo
gleich 12
? Was ist die Bedeutung davon?
Und da Sie dies bereits entschieden haben, was versuchen Sie zu erreichen, indem Sie einen Test schreiben, der bestätigt, dass er BarDerived
immer foo
gleich ist 12
?
Dies wird noch schlimmer, wenn Sie anfangen zu berücksichtigen, dass doThings()
dies in einer abgeleiteten Klasse überschrieben werden kann. Stellen Sie AnotherDerived
sich vor, Sie würden überschreiben, doThings()
damit es immer zurückkehrt foo * 2
. Jetzt haben Sie eine Klasse, die fest codiert ist Base(12)
und deren doThings()
Wert 24 ist. Obwohl sie technisch testbar ist, hat sie keine kontextbezogene Bedeutung. Der Test ist nicht nachvollziehbar.
Ich kann mir wirklich keinen Grund vorstellen, diesen fest codierten Wertansatz zu verwenden. Selbst wenn es einen gültigen Anwendungsfall gibt, verstehe ich nicht, warum Sie versuchen, einen Test zu schreiben, um diesen fest codierten Wert zu bestätigen . Es gibt nichts zu gewinnen, wenn getestet wird, ob ein konstanter Wert dem gleichen konstanten Wert entspricht.
Jeder Testfehler beweist von Natur aus, dass der Test falsch ist . Es gibt kein Ergebnis, bei dem ein Testfehler beweist, dass die Geschäftslogik falsch ist. Sie können effektiv nicht bestätigen, welche Tests erstellt wurden, um sie überhaupt zu bestätigen.
Das Problem hat nichts mit Vererbung zu tun, falls Sie sich fragen. Sie haben zufällig einen const-Wert im Basisklassenkonstruktor verwendet, aber Sie hätten diesen const-Wert an einer anderen Stelle verwenden können, und dann wäre er nicht mit einer geerbten Klasse verbunden.
Bearbeiten
Es gibt Fälle, in denen fest codierte Werte kein Problem darstellen. (Nochmals, entschuldigen Sie die C # -Syntax, aber das Prinzip ist immer noch dasselbe)
public class Base
{
public int MultiplyFactor;
protected int InitialValue;
public Base(int value, int factor)
{
this.InitialValue = value;
this.MultiplyFactor= factor;
}
public int GetMultipliedValue()
{
return this.InitialValue * this.MultiplyFactor;
}
}
public class DoublesYourNumber : Base
{
public DoublesYourNumber(int value) : base(value, 2) {}
}
public class TriplesYourNumber : Base
{
public TriplesYourNumber(int value) : base(value, 3) {}
}
Während der konstante Wert ( 2
/ 3
) noch den Ausgabewert von beeinflusst GetMultipliedValue()
, hat der Verbraucher Ihrer Klasse auch noch die Kontrolle darüber!
In diesem Beispiel können noch aussagekräftige Tests geschrieben werden:
var inputValue = 123;
var expectedDoubledOutputValue = inputValue * 2;
var receivedDoubledOutputValue = new DoublesYourNumber(inputValue).GetMultipliedValue();
Assert.AreEqual(expectedDoubledOutputValue , receivedDoubledOutputValue);
var expectedTripledOutputValue = inputValue * 3;
var receivedTripledOutputValue = new TriplesYourNumber(inputValue).GetMultipliedValue();
Assert.AreEqual(expectedTripledOutputValue , receivedTripledOutputValue);
- Technisch gesehen schreiben wir immer noch einen Test, der prüft, ob die const in
base(value, 2)
mit der const in übereinstimmt inputValue * 2
.
- Gleichzeitig testen wir jedoch auch, ob diese Klasse einen bestimmten Wert korrekt mit diesem vorgegebenen Faktor multipliziert .
Der erste Aufzählungspunkt ist für den Test nicht relevant. Der zweite ist!
virtual
?