0

I am using QTest but this issue is, in my opinion, more general than simply QTest specific. Suppose we have a simple class:

class Number
{
    Number() {}
    Number(int i) : m_Value(i) {}

    int value() const { return m_Value; }

private:
    int m_Value = 0;
};

And another class that derives from Number:

class Ten : public Number
{
public:
    Ten() : Number(10) {}
};

The test (using QTest) of class Number might look like this:

class NumberTest : public QObject
{
    Q_OBJECT
public:
    using QObject::QObject;

private slots:
    void testNumberConstructor()
    {
        std::shared_ptr<Number> num = createNumber();
        QVERIFY(num->value() == 0); 
        std::shared_ptr<Number> num = createNumber(1);
        QVERIFY(num->value() == 1);        
    }

private:
    virtual std::shared_ptr<Number> createNumber() { return std::make_shared<Number>(); }
    virtual std::shared_ptr<Number> createNumber(int i) { return std::make_shared<Number>(i); }
};

And that allows us to derive the test of Ten from NumberTest to ensure that Ten did not inadvertently break Number's interface's contract (might be important for example if Number invoked virtual methods overridden by Ten or if later it was changed to do that).

class TenTest : public NumberTest
{
    Q_OBJECT
public:
    using NumberTest::NumberTest;

private slots:
    void testTenConstructor()
    {
        Ten num;
        QVERIFY(num.value() == 10);
    };

private:
    virtual std::shared_ptr<Number> createNumber() override { return std::make_shared<Number>(new Ten()); }
    virtual std::shared_ptr<Number> createNumber(int i) override { return std::make_shared<Number>(new Ten()); } //<<< This is the problem!
};

NOTE: I use shared_ptr because unique_ptr does not auto-capture the correct destructor and I do not have virtual destructor here.

The inherited test will run both base class's test(s) and its own ones but it will fail on the second assert (QVERIFY) of the base class's test because the derived class Ten does not provide constructor (or any other way) to provide what the test requires (Number with specific value). This issue arises pretty much only when testing constructors of inherited classes (when inheriting the tests) and I am unsure how to best solve it.

I certainly do not want to cherry pick from the parent's test which tests will be run and which ones not or even worse copy the tests (omitting the constructor's test).

On the other hand the only solution I can think of is to omit the test of constructor(s) from the test class that will be inherited (and should be common for all tests that derive from it) and create separate test class for the base class only that will test its constructors. After all constructors are not really part of the interface. Is there any other way to (elegantly) solve this issue? Or am I doing it completely wrong to begin with?

Resurrection
  • 3,916
  • 2
  • 34
  • 56
  • Why using `createNumber()` in `testNumberConstructor` ? – Jarod42 Dec 01 '16 at 09:08
  • @Jarod42 Because when you inherit the constructor(s) it would be sufficient to provide your class instead of the base class. However you are right it might be better to hard-code there the base class and let the constructor testing be done separately for each class. Only drawback I see with it is that the derived test would test base class' constructor(s) that are irrelevant for derived test. – Resurrection Dec 01 '16 at 10:02

0 Answers0