Anda memiliki konflik di sini.
Anda ingin menguji nilai kembali dari doThings()
, yang bergantung pada literal (nilai const).
Setiap tes yang Anda tulis untuk ini secara inheren akan mendidih menguji nilai const , yang tidak masuk akal.
Untuk menunjukkan kepada Anda contoh yang lebih masuk akal (saya lebih cepat dengan C #, tetapi prinsipnya sama)
public class TriplesYourInput : Base
{
public TriplesYourInput(int input)
{
this.foo = 3 * input;
}
}
Kelas ini dapat diuji secara bermakna:
var inputValue = 123;
var expectedOutputValue = inputValue * 3;
var receivedOutputValue = new TriplesYourInput(inputValue).doThings();
Assert.AreEqual(receivedOutputValue, expectedOutputValue);
Ini lebih masuk akal untuk diuji. Keluarannya didasarkan pada input yang Anda pilih untuk diberikan. Dalam kasus seperti itu, Anda dapat memberikan kelas input yang dipilih secara sewenang-wenang, mengamati hasilnya, dan menguji apakah kelas itu sesuai dengan harapan Anda.
Beberapa contoh prinsip pengujian ini. Perhatikan bahwa contoh saya selalu memiliki kendali langsung atas apa input dari metode yang dapat diuji.
- Tes jika
GetFirstLetterOfString()
mengembalikan "F" ketika saya memasukkan "Flater".
- Tes jika
CountLettersInString()
mengembalikan 6 ketika saya memasukkan "Flater".
- Tes jika
ParseStringThatBeginsWithAnA()
mengembalikan pengecualian ketika saya memasukkan "Flater".
Semua tes ini dapat memasukkan nilai apa pun yang mereka inginkan , selama harapan mereka sesuai dengan apa yang mereka masukkan.
Tetapi jika output Anda ditentukan oleh nilai konstan, maka Anda harus membuat ekspektasi konstan, dan kemudian menguji apakah yang pertama cocok dengan yang kedua. Yang konyol, ini selalu atau tidak akan pernah terjadi; tak satu pun dari keduanya merupakan hasil yang bermakna.
Beberapa contoh prinsip pengujian ini. Perhatikan bahwa contoh-contoh ini tidak memiliki kendali atas setidaknya satu dari nilai-nilai yang sedang dibandingkan.
- Tes jika
Math.Pi == 3.1415...
- Tes jika
MyApplication.ThisConstValue == 123
Tes-tes ini untuk satu nilai tertentu. Jika Anda mengubah nilai ini, tes Anda akan gagal. Intinya, Anda tidak menguji apakah logika Anda berfungsi untuk input yang valid, Anda hanya menguji apakah seseorang dapat memprediksi hasil yang secara akurat tidak dapat mereka kendalikan.
Itu pada dasarnya menguji pengetahuan penulis tes tentang logika bisnis. Itu tidak menguji kode, tetapi penulis sendiri.
Mengembalikan ke contoh Anda:
class BarDerived : public Base
{
public:
BarDerived() : Base(12) { };
~BarDerived() { };
int doBarThings() { return foo + 1; };
}
Kenapa BarDerived
harus selalu foo
sama dengan 12
? Apa artinya ini?
Dan mengingat bahwa Anda sudah memutuskan ini, apa yang Anda coba dapatkan dengan menulis tes yang menegaskan bahwa BarDerived
selalu foo
sama dengan 12
?
Ini menjadi lebih buruk jika Anda mulai memfaktorkan yang doThings()
dapat ditimpa dalam kelas turunan. Bayangkan jika AnotherDerived
ditimpa doThings()
sehingga selalu kembali foo * 2
. Sekarang, Anda akan memiliki kelas yang hardcoded sebagai Base(12)
, yang doThings()
nilainya 24. Meskipun secara teknis dapat diuji, itu tidak memiliki makna kontekstual. Tes ini tidak dapat dipahami.
Saya benar-benar tidak bisa memikirkan alasan untuk menggunakan pendekatan nilai hardcoded ini. Bahkan jika ada use case yang valid, saya tidak mengerti mengapa Anda mencoba menulis tes untuk mengonfirmasi nilai hardcoded ini . Tidak ada untungnya dengan menguji jika nilai konstan sama dengan nilai konstan yang sama.
Kegagalan tes apa pun secara inheren membuktikan bahwa tes itu salah . Tidak ada hasil di mana kegagalan pengujian membuktikan bahwa logika bisnis salah. Anda secara efektif tidak dapat mengkonfirmasi tes mana yang dibuat untuk mengonfirmasi di tempat pertama.
Masalahnya tidak ada hubungannya dengan warisan, jika Anda bertanya-tanya. Anda kebetulan menggunakan nilai const di konstruktor kelas dasar, tetapi Anda bisa menggunakan nilai const ini di tempat lain dan kemudian itu tidak akan terkait dengan kelas yang diwarisi.
Edit
Ada kasus di mana nilai hardcoded tidak menjadi masalah. (sekali lagi, maaf untuk sintaks C # tetapi prinsipnya masih sama)
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) {}
}
Sementara nilai konstan ( 2
/ 3
) masih mempengaruhi nilai output GetMultipliedValue()
, konsumen kelas Anda masih memiliki kendali atas itu juga!
Dalam contoh ini, tes yang berarti masih dapat ditulis:
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);
- Secara teknis, kami masih menulis tes yang memeriksa apakah const in
base(value, 2)
cocok dengan const in inputValue * 2
.
- Namun, kami pada saat yang sama juga menguji bahwa kelas ini dengan benar mengalikan nilai yang diberikan oleh faktor yang telah ditentukan ini .
Poin pertama tidak relevan untuk diuji. Yang kedua adalah!
virtual
?