Unit Testing Abstract Classes with Google Mock in C++

abstract classcgtestmockingunit testing

I want to test an abstract class with:

  1. Pure virtual methods that should be overridden in sub-classes
  2. Non-pure virtual methods that use the pure virtual methods (as opposed to this question)
class Fu
{
  public:
    virtual void func(int count, std::string data)
    {
        for(int i = 0; i < count; i++)
        {
            pureFunc(data);
        }

    }
    virtual void pureFunc(std::string data) = 0;
}

Now to test func, I want to make sure that it calls pureFunc count times with argument data. What I have done is to subclass Fu and also create a Mock:

class TestFu : public Fu
{
  public:
    virtual void pureFunc(std::string data) override {};
}

class MockFu : public TestFu
{
  public:
    MOCK_METHOD1(pureFunc, void(std::string));
}

And to test I do the following:

MockFu mock_fu;
EXPECT_CALL(mock_fu, pureFunc("test"))
.Times(10);

mock_fu.func(10, "test");

However I was wondering if the above is a valid pattern. My worry is that I am testing the mock class as opposed to the class or it's non-abstract sub-class.

To sum up:

  1. Is the above pattern valid? If yes is it ok that I am testing a mock object? If no how do I achieve my goal?
  2. In general is it valid practice to mock some parts of a class to unit test other parts? If so what is the common practice to do so?

Best Answer

You are asking quite a few things here, but let me share my opinion on the things you describe.

The typical use of mock classes is when you need them for testing another class, and need to check if the state of the mock class is changed (instead of more heavy implementations of the interface).

The case that you describe is valid of course, but I would like to argue that the test in itself is not useful. In a typical example I would expect pureFunc to be private, but it does not matter too much. What you are actually testing now is implementation, and not the behavior. What you should test is what func does (i.e., by returning something or changing the state of the class).

To be more explicit. In your example I can have the for-loop in func run to count/2 and run the code in pureFunc itself twice (doesn't work for odd numbers, but consider it for the sake of the argument. The unit test fails, however, the state of your object is the same. You want to be able to change your class and implementation, without unit tests starting to fail. If you need to modify the unit test everytime you change the implementation, you have done something wrong.

So what can you do instead? Well, you have implementations of your abstract class. You can test func for that, to check if it does what you expect. (And pureFunc if it is really public).