You have a conflict here.
You want to test the return value of doThings()
, which relies on a literal (const value).
Any test you write for this is inherently going to boil down testing a const value, which is nonsensical.
To show you a more sensical example (I'm quicker with C#, but the principle is the same)
public class TriplesYourInput : Base
{
public TriplesYourInput(int input)
{
this.foo = 3 * input;
}
}
This class can be tested meaningfully:
var inputValue = 123;
var expectedOutputValue = inputValue * 3;
var receivedOutputValue = new TriplesYourInput(inputValue).doThings();
Assert.AreEqual(receivedOutputValue, expectedOutputValue);
This makes more sense to test. Its output is based on the input that you chose to give it. In such a case, you can give a class an arbitrarily chosen input, observe its output, and test if it matches your expectations.
Some examples of this testing principle. Notice that my examples always have direct control over what the input of the testable method is.
- Test if
GetFirstLetterOfString()
return "F" when I input "Flater".
- Test if
CountLettersInString()
returns 6 when I input "Flater".
- Test if
ParseStringThatBeginsWithAnA()
returns an exception when I input "Flater".
All of these tests can input whatever value they want, as long as their expectations are in line with what they're inputting.
But if your output is decided by a constant value, then you're going to have to create a constant expectation, and then test if the first matches the second. Which is silly, this is either always or never going to pass; neither of which is a meaningful result.
Some examples of this testing principle. Notice that these examples have no control over at least one of the values that are being compared.
- Test if
Math.Pi == 3.1415...
- Test if
MyApplication.ThisConstValue == 123
These tests for one particular value. If you change this value, your tests will fail. In essence, you're not testing whether your logic works for any valid input, you're simply testing if someone is able to accurately predict an outcome which they have no control over.
That's essentially testing the test writer's knowledge of the business logic. It's not testing the code, but the writer themselves.
Reverting back to your example:
class BarDerived : public Base
{
public:
BarDerived() : Base(12) { };
~BarDerived() { };
int doBarThings() { return foo + 1; };
}
Why does BarDerived
always have a foo
equal to 12
? What is the meaning of this?
And given that you've already decided this, what are you trying to gain by writing a test that confirms that BarDerived
always has a foo
equal to 12
?
This gets even worse if you start factoring in that doThings()
can be overridden in a derived class. Imagine if AnotherDerived
were to override doThings()
so that it always returns foo * 2
. Now, you're going to have a class which is hardcoded as Base(12)
, whose doThings()
value is 24. While technically testable, it is devoid of any contextual meaning. The test is not comprehensible.
I genuinely can't think of a reason to use this hardcoded value approach. Even if there is a valid use case, I don't understand why you're trying to write a test to confirm this hardcoded value. There is nothing to gain by testing if a constant value equals the same constant value.
Any test failure inherently proves that the test is wrong. There is no outcome where a test failure proves that the business logic is wrong. You're effectively unable to confirm that which tests are created to confirm in the first place.
The issue has nothing to do with inheritance, in case you were wondering. You just happen to have used a const value in the base class constructor, but you could've used this const value anywhere else and then it wouldn't be related to an inherited class.
Edit
There are cases where hardcoded values are not a problem. (again, sorry for the C# syntax but the principle is still the same)
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) {}
}
While the constant value (2
/3
) is still influencing the output value of GetMultipliedValue()
, the consumer of your class still has control over it too!
In this example, meaningful tests can still be written:
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);
- Technically, we're still writing a test that checks if the const in
base(value, 2)
matches the const in inputValue * 2
.
- However, we are at the same time also testing that this class is correctly multiplying any given value by this predetermined factor.
The first bullet point isn't relevant to test. The second is!
virtual
? – Deduplicator Apr 17 '18 at 10:02BarDerived
andBase
may have once been the same class. When a similar functionality was to be added the common part was moved into the Base class, with different specializations implemented in each Derived class. – CharonX Apr 17 '18 at 10:20