views:

242

answers:

5

I have a code base where many of the classes I implement derive from classes that are provided by other divisions of my company. Working with these other devisions often have the working relationship as though they are third party middle ware vendors.

I'm trying to write test code without modifying these base classes. However, there are issues with creating meaningful test objects due to the lack of interfaces:

//ACommonClass.h
#include "globalthermonuclearwar.h" //which contains deep #include dependencies...
#include "tictactoe.h" //...and need to exist at compile time to get into test...

class Something //which may or may not inherit from another class similar to this...
{
public:
  virtual void fxn1(void);  //which often calls into many other classes, similar to this
  //...
  int data1;  //will be the only thing I can test against, but is often meaningless without fxn1 implemented
  //...
};

I'd normally extract an interface and work from there, but as these are "Third Party", I can't commit these changes.

Currently, I've created a separate file that holds fake implementations for functions that are defined in the third-party supplied base class headers on a need to know basis, as has been described in the book "Working with Legacy Code".

My plan was to continue to use these definitions and provide alternative test implementations for each third party class that I needed:

//SomethingRequiredImplementations.cpp
#include "ACommonClass.h"
void CGlobalThermoNuclearWar::Simulate(void) {};  // fake this and all other required functions...
// fake implementations for otherwise undefined functions in globalthermonuclearwar.h's #include files...
void Something::fxn1(void) { data1 = blah(); } //test specific functionality.

But before I start doing that I was wondering if any one has tried providing actual objects on a code base similar to mine, which would allow creating new test specific classes to use in place of actual third-party classes.

Note all code bases in question are written in C++.

A: 

You might want to consider mocking instead of faking as a potential solution. In some cases you may need to write wrapper classes that are mockable if the original classes aren't. I've done this with framework classes in C#/.Net, but not C++ so YMMV.

tvanfosson
+1  A: 

Mock objects are suitable for this kind of task. They allow you to simulate the existence of other components without needing them to be present. You simply define the expected input and output in your tests.

Google have a good mocking framework for C++.

seanhodges
A: 

If I have a class that I need under test that derives from something I can't (or don't want to) run under test I'll:

  1. Make a new logic-only class.
  2. Move the code-i-wanna-test to the logic class.
  3. Use an interface to talk back to the real class to interact with the base class and/or things I can't or won't put in the logic.
  4. Define a test class using that same interface. This test class could have nothing but noops or fancy code that simulates the real classes.

If I have a class that I just need to use in testing, but using the real class is a problem (dependencies or unwanted behaviors):

  1. I'll define a new interface that looks like all of the public methods I need to call.
  2. I'll create a mock version of the object that supports that interface for testing.
  3. I'll create another class that is constructed with a "real" version of that class. It also supports that interface. All interface calls a forwarded to the real object methods.
  4. I'll only do this for methods I actually call - not ALL the public methods. I'll add to these classes as I write more tests.

For example, I wrap MFC's GDI classes like this to test Windows GDI drawing code. Templates can make some of this easier - but we often end up not doing that for various technical reasons (stuff with Windows DLL class exporting...).

I'm sure all this is in Feather's Working with Legacy Code book - and what I'm describing has actual terms. Just don't make me pull the book off the shelf...

Aardvark
A: 

One thing you did not indicate in your question is the reason why your classes derive from base classes from the other division. Is the relationship really a IS-A relationshiop ?

Unless your classes needs to be used by a framework, you could consider favoring delegation over inheritance. Then you can use dependency injection to provide your class with a mock of their class in the unit tests.

Otherwise, an idea would be to write a script to extract and create the interface your need from the header they provide, and integrate this to the compilation process so your unit test can ve checked in.

philippe
A: 

I'm running into a very similar problem at the moment. I don't want to add a bunch of interfaces that are only there for the purpose of testing, so I can't use any of the existing mock object libraries. To get around this I do the same thing, creating a different file with fake implementations, and having my tests link the fake behaviour, and production code links the real behaviour.

What I wish I could do at this point, is take the internals of another mock framework, and use it inside my fake objects. It would look a little something like this:

Production.h

class ConcreteProductionClass { // regular everyday class
protected:
    ConcreteProductionClass(); // I've found the 0 arg constructor useful
public:
    void regularFunction(); // regular function that I want to mock
}

Mock.h

class MockProductionClass 
    : public ConcreteProductionClass
    , public ClassThatLetsMeSetExpectations 
{
    friend class ConcreteProductionClass;
    MockTypes membersNeededToSetExpectations;
public:
    MockClass() : ConcreteProductionClass() {}
}

ConcreteProductionClass::regularFunction() {
    membersNeededToSetExpectations.PassOrFailTheTest();
}

ProductionCode.cpp

void doSomething(ConcreteProductionClass c) {
    c.regularFunction();
}

Test.cpp

TEST(myTest) {
    MockProductionClass m;
    m.SetExpectationsAndReturnValues();
    doSomething(m);
    ASSERT(m.verify());
}

The most painful part of all this is that the other mock frameworks are so close to this, but don't do it exactly, and the macros are so convoluted that it's not trivial to adapt them. I've begun looking into this on my spare time, but it's not moving along very quickly. Even if I got my method working the way I want, and had the expectation setting code in place, this method still has a couple drawbacks, one of them being that your build commands can get to be kind of long if you have to link against a lot of .o files rather than one .a, but that's manageable. It's also impossible to fall through to the default implementation, since we're not linking it. Anyway, I know this doesn't answer the question, or really even tell you anything you don't already know, but it shows how close the C++ community is to being able to mock classes that don't have a pure virtual interface.

I made a mistake in this, but I don't feel like editing it. Making the base class a friend doesn't work there needs to be an extern declaration of the internals of the mock so that they can be used in the new bodies of the base class functions. Then in your main.cpp, or whatever is running the test suite, you have the instance of the internal objects.