Bạn có một cuộc xung đột ở đây.
Bạn muốn kiểm tra giá trị trả về của doThings()
, giá trị này dựa trên giá trị bằng chữ (const).
Bất kỳ bài kiểm tra nào bạn viết cho điều này vốn dĩ sẽ sôi sục khi kiểm tra một giá trị const , điều này là vô nghĩa.
Để cho bạn thấy một ví dụ hợp lý hơn (Tôi nhanh hơn với C #, nhưng nguyên tắc là như nhau)
public class TriplesYourInput : Base
{
public TriplesYourInput(int input)
{
this.foo = 3 * input;
}
}
Lớp này có thể được kiểm tra một cách có ý nghĩa:
var inputValue = 123;
var expectedOutputValue = inputValue * 3;
var receivedOutputValue = new TriplesYourInput(inputValue).doThings();
Assert.AreEqual(receivedOutputValue, expectedOutputValue);
Điều này có ý nghĩa hơn để kiểm tra. Đầu ra của nó dựa trên đầu vào mà bạn đã chọn để cung cấp cho nó. Trong trường hợp như vậy, bạn có thể cung cấp cho một lớp một đầu vào được chọn tùy ý, quan sát đầu ra của nó và kiểm tra xem nó có phù hợp với mong đợi của bạn không.
Một số ví dụ về nguyên tắc thử nghiệm này. Lưu ý rằng các ví dụ của tôi luôn có quyền kiểm soát trực tiếp đối với đầu vào của phương thức có thể kiểm tra là gì.
- Kiểm tra nếu
GetFirstLetterOfString()
trả về "F" khi tôi nhập "Flater".
- Kiểm tra nếu
CountLettersInString()
trả về 6 khi tôi nhập "Flater".
- Kiểm tra nếu
ParseStringThatBeginsWithAnA()
trả về một ngoại lệ khi tôi nhập "Flater".
Tất cả các thử nghiệm này có thể nhập bất kỳ giá trị nào họ muốn , miễn là kỳ vọng của họ phù hợp với những gì họ đang đưa vào.
Nhưng nếu đầu ra của bạn được quyết định bởi một giá trị không đổi, thì bạn sẽ phải tạo ra một kỳ vọng không đổi, sau đó kiểm tra xem đầu tiên có khớp với giá trị thứ hai không. Điều này thật ngớ ngẩn, điều này luôn luôn hoặc sẽ không bao giờ vượt qua; không phải là một kết quả có ý nghĩa
Một số ví dụ về nguyên tắc thử nghiệm này. Lưu ý rằng các ví dụ này không có quyền kiểm soát đối với ít nhất một trong các giá trị đang được so sánh.
- Kiểm tra nếu
Math.Pi == 3.1415...
- Kiểm tra nếu
MyApplication.ThisConstValue == 123
Những thử nghiệm cho một giá trị cụ thể. Nếu bạn thay đổi giá trị này, các bài kiểm tra của bạn sẽ thất bại. Về bản chất, bạn không kiểm tra xem logic của bạn có hoạt động cho bất kỳ đầu vào hợp lệ nào không, bạn chỉ đang kiểm tra xem ai đó có thể dự đoán chính xác kết quả mà họ không kiểm soát được hay không.
Điều đó về cơ bản là kiểm tra kiến thức của người viết bài kiểm tra về logic kinh doanh. Đó không phải là kiểm tra mã, mà là chính người viết.
Trở lại ví dụ của bạn:
class BarDerived : public Base
{
public:
BarDerived() : Base(12) { };
~BarDerived() { };
int doBarThings() { return foo + 1; };
}
Tại sao BarDerived
luôn luôn có một foo
bằng 12
? Ý nghĩa của việc này là gì?
Và cho rằng bạn đã quyết định điều này, bạn đang cố gắng đạt được điều gì bằng cách viết một bài kiểm tra xác nhận BarDerived
luôn luôn có giá trị foo
bằng 12
?
Điều này thậm chí còn tồi tệ hơn nếu bạn bắt đầu bao thanh toán trong đó doThings()
có thể bị ghi đè trong một lớp dẫn xuất. Hãy tưởng tượng nếu AnotherDerived
được ghi đè doThings()
để nó luôn trở lại foo * 2
. Bây giờ, bạn sẽ có một lớp được mã hóa cứng Base(12)
, có doThings()
giá trị là 24. Trong khi có thể kiểm tra về mặt kỹ thuật, nó không có bất kỳ ý nghĩa ngữ cảnh nào. Bài kiểm tra không thể hiểu được.
Tôi thực sự không thể nghĩ ra lý do để sử dụng phương pháp giá trị được mã hóa cứng này. Ngay cả khi có trường hợp sử dụng hợp lệ, tôi không hiểu tại sao bạn lại cố gắng viết bài kiểm tra để xác nhận giá trị được mã hóa cứng này . Không có gì đạt được bằng cách kiểm tra nếu một giá trị không đổi bằng cùng một giá trị không đổi.
Bất kỳ thử nghiệm thất bại vốn đã chứng minh rằng thử nghiệm là sai . Không có kết quả trong đó một thử nghiệm thất bại chứng minh rằng logic kinh doanh là sai. Bạn thực sự không thể xác nhận rằng thử nghiệm nào được tạo để xác nhận ngay từ đầu.
Vấn đề không liên quan gì đến thừa kế, trong trường hợp bạn đang tự hỏi. Bạn tình cờ đã sử dụng một giá trị const trong hàm tạo của lớp cơ sở, nhưng bạn có thể đã sử dụng giá trị const này ở bất kỳ nơi nào khác và sau đó nó sẽ không liên quan đến một lớp được kế thừa.
Biên tập
Có những trường hợp giá trị mã hóa cứng không phải là vấn đề. (một lần nữa, xin lỗi vì cú pháp C # nhưng nguyên tắc vẫn giống nhau)
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) {}
}
Trong khi giá trị không đổi ( 2
/ 3
) vẫn ảnh hưởng đến giá trị đầu ra GetMultipliedValue()
, thì người tiêu dùng của lớp bạn cũng có quyền kiểm soát nó!
Trong ví dụ này, các bài kiểm tra có ý nghĩa vẫn có thể được viết:
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);
- Về mặt kỹ thuật, chúng tôi vẫn đang viết một bài kiểm tra để kiểm tra xem const in có
base(value, 2)
khớp với const trong không inputValue * 2
.
- Tuy nhiên, chúng tôi đồng thời cũng kiểm tra rằng lớp này đang nhân chính xác bất kỳ giá trị đã cho nào với yếu tố được xác định trước này .
Điểm đạn đầu tiên không liên quan để kiểm tra. Thứ hai là!
virtual
?