开发者

Does Mock Objects in C++ Always Requires Virtual Methods or Templates?

Suppose I have classes

class Inner {
  public:
    void doSomething();
};

class Outer {
  public:
    Outer(Inner *inner);  // Dependency injection.

    void callInner();
};

Proper unit-testing says I should have tests for Inner. Then, I should have tests for Outer that uses not a real Inner but rather a MockInner so that I would be performing unit-tests on the functionality added by just Outer instead of the full stack Outer/Inner.

To do so, Googletest seems to suggest turning Inner into a pure abstract class (interface) like this:

// Introduced merely for the sake of unit-testing.
struct InnerInterface {
  void doSomething() = 0;
};

// Used in production.
class Inner : public InnerInterface {
  public:
    /* override */ void doSomething();
};

// Used in unit-tests.
class MockInner : public InnerInterface {
  public:
    /* override */ void doSomething();
};

class Outer {
  public:
    Outer(Inner *inner);  // Dependency injection.

    void callInner();
};

So, in production code, I would use Outer(new Inner); while in test, Outer(new MockInner).

OK. Seems nice in theory, but when I started using this idea throughout the code, I find myself creating a pure abstract class for every freaking class. 开发者_StackOverflowIt's a lot of boiler-plate typing, even if you can ignore the slight run-time performance degradable due to the unnecessary virtual dispatch.

An alternative approach is to use templates as in the following:

class Inner {
  public:
    void doSomething();
};

class MockInner {
  public:
    void doSomething();
};

template<class I>
class Outer {
  public:
    Outer(I *inner);

    void callInner();
};

// In production, use
Outer<Inner> obj;

// In test, use
Outer<MockInner> test_obj;

This avoids the boiler-plating and the unnecessary virtual dispatch; but now my entire codebase is in the freaking header files, which makes it impossible to hide source implementations (not to mention dealing with frustrating template compilation errors and the long build time).

Are those two methods, virtuals and templates, the only ways to do proper unit-testing? Are there better ways to do proper unit-testing?

By proper unit-testing, I mean each unit-test tests only the functionalities introduced by that unit but not the unit's dependencies also.


I don't think you must mock out every dependency of your tested class in practice. If it is complicated to create, use or sense through, then yes. Also if it directly depends on some unneeded external resource such as a DB, network or filesystem.

But if none of these is an issue, IMO it is OK to just use an instance of it directly. As you already unit tested it, you can be reasonably sure that it works as expected and doesn't interfere with higher-level unit tests.

I personally prefer working unit tests and simple, clean, maintainable design over adhering to some ideal set up by unit test purists.

each unit-test tests only the functionalities introduced by that unit but not the unit's dependencies also.

Using a functionality and testing a functionality are two very different things.


I also think that using an instance on Inner directly is OK. My problem is mocking external objects that are not part of my code (provided through static libraries or DLLs, sometimes 3rd party). I am inclined to rewrite a mock DLL or library with the same class names, and then linking differently for test. Modifying the header file of the external dependency to add "virtual"s seems unacceptable to me. Does anyone have a better solution?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜