3

I'm currently refactoring an existing design, which was created without TDD. There is a class hierarchy with one abstract base class and two subclasses. In the original design, these classes were mostly just data holders without much behavior. I have identified some functionality which should be implemented in the base class, and I'd like to write tests for this functionality now. But since the class is abstract, I cannot instantiate it (obviously).

Note: The functionality I'd like to test doesn't invoke any pure virtual methods.

class Base {}; // Is abstract

TEST(BaseTest, doesSomethingAmazing) {
    Base aBase; // <-------- Not possible!!! 
    ASSERT_THAT(aBase.amazeMe(), Eq(AMAZING_RESULT));
}

Edit: To clarify a few things:

  • Inheritance does actually make sense in this situation - both subclasses map to specific domain concepts, and polymorphism helps keep the surrounding code clean
  • There is behavior which will be used in both subclasses, and which needs data that is common to both classes. So I think it makes sense to put it in a common super class.

I can think of several possible solutions, but none of them seems optimal to me:

  • Add a subclass to the test code, which implements all pure virtual functions. Downside: Hard to name that subclass in a concise way, understanding the tests becomes harder
  • Instantiate an object of the subclass instead. Downside: Makes the tests pretty confusing
  • Add empty implementations to the base class. Downside: Class is not abstract anymore

I tend towards option 3, to make the tests as clear as possible, but I'm not really satisfied with that. Is there a better way I'm not aware of?

  • 2
    Are you using a mocking framework? One idea is to create a mock of Base and then call the appropriate method. Alternatively, maybe you could factor out the method and put it in a separate helper class so it's easier to test? – George Howarth Apr 14 '14 at 13:49
  • 1
    @pdr, are you suggesting that putting behaviour in a baseclass is wrong? If each subclass will have to reimplement the same behaviour then I think it's fully justified to push that behaviour into the baseclass. Even if it's abstract. – MetaFight Apr 14 '14 at 15:17
  • 1
    @pdr it makes no sense for abstract classes to be instantiated, but that doesn't mean they should have no behavior, AFAIK. I mean how would you implement things like the "Template method" pattern if you cannot have behavior on abstract classes? – lethal-guitar Apr 14 '14 at 15:17
  • 1
    @MetaFight: No, I'm not suggesting that. I'm suggesting that if I call DoStuff() on a derived class, the expected behaviour (which is what I should be testing) is a behaviour of that derived class, even if it is ENTIRELY implemented in the base class. I am distinguishing between behaviour and implementation details. That is, if I decide later that DoStuff() should be implemented in the derived class, or a helper class, instead then the behaviour of that class doesn't change (though other derived classes might) and neither should the tests. – pdr Apr 14 '14 at 16:30
  • 2
    Yeah, that's one of those cost-benefit decisions you have to make sometimes. I would test both, doing whatever I could to remove duplication from the test code. I don't know C++ testing frameworks very well, but in NUnit, you can have base test classes from which you can derive others, so you can very easily say "all classes that derive from type A must satisfy these conditions". Then if, later, that becomes untrue -- eg. if the implementation is moved to the derived classes -- you can also move the test to the derived test classes and run before changing your code. – pdr Apr 15 '14 at 10:12
2

As long as the functionality you'd like to test doesn't invoke any pure virtual methods and isn't overridden in subclasses, I'd stick with your bullet point number 1 - creating a test-specific subclass. You could name it Sub<ClassName> or any generic name reflecting that the subclass itself is not important.

Now if what you want to test can be altered in any way by the final concrete class, you might want to consider this :

I don't know if there's an equivalent in the C++ test framework you're using, but NUnit (and I believe JUnit) let you implement the Abstract Test pattern.

In short, you have

  • An abstract base test class. It contains a abstract factory method used to generate the object under test, and the test methods per se. These methods verify things that must stay true regardless of the concrete object underneath.

  • As many derived test classes as there are derivatives of the abstract class under test. You just have to implement the factory method here to return an instance of the derived class.

When the test runner sees a derived test class, it will automatically execute all test methods inherited from the base test class against the factory method-generated object.

  • Just realized I partly paraphrased @pdr 's last comment. Sorry about that :/ – guillaume31 Apr 18 '14 at 14:47
1

You could do the following:

  • create an abstract base class with only pure virtual methods, for example AmazingObjectInterface;
  • add a subclass called BasicAmazingObject that defines the non-virtual methods that you need to test;
  • finally, your 2 original subclasses becomes subclasses of BasicAmazingObject.

This allows you to separate what varies and test what is needed properly. Obviously, yo would introduce then one more class, but this is the price to pay for the lack of interface support (in the java sense) in C++.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.