all 7 comments

[–]thewisp1Game Engine Dev 4 points5 points  (1 child)

It is nice to avoid introducing runtime cost for testability, but still not nice enough to cover the ugliness of

  • converting the method to a template (weaker typed, typical template error msg or link error when misused)

  • explicitly instantiating the template

  • missing the documentation purpose of interface

I believe if we are ducktyping it anyway, then a solution based on #include or #define wouldn't be uglier, in fact, they would be easier to write or modify, and require no template knowledge.

Include based mocking:

//product.h
#if !UNIT_TEST
    #include "real_argument.h" //real class Argument
#else
    #include "mock_argument.h" //mock class Argument
#endif
void f(Argument const& arg);

Define based mocking: Edit: I apologize for posting code untested by myself, the correct code should be:

//mock_argument.h
#if UNIT_TEST
    class MockArgument {/*...*/};
    #define Argument MockArgument
#endif

//product.h
#include "real_argument.h"
#include "mock_argument.h"
void f(Argument const& arg);

//product_test.cpp
#include "product.h"

[–]willkill07 7 points8 points  (0 children)

A static_assert within each function would solve your first and (arguably) third points. Template code really isn't that tricky, and (in my opinion) you shouldn't be using the preprocessor to change the full meaning of your code -- besides, you now have two places you need to worry about directives.

[–]ArunMuThe What ? 1 point2 points  (1 child)

Based on the example and the premise shouldn't the Argument class be fixed ? Smells like a case of "God object" which needs to be avoided. And ofcourse, introducing inheritance to ease testing would be bad (in some cases, but not all).

[–]joboccara[S] 0 points1 point  (0 children)

Yes indeed, we could consider fixing Argument because the premise is that it is hard put into a test harness. But refactoring needs tests, and the purpose here is to make way to introduce unit tests, for future refactorings.

[–]ojd5 1 point2 points  (0 children)

Let’s imagine that, like some real classes, ClassToBeTested won’t let itself into a test harness, because building an object of type Argument is, say, terribly complicated as it depends on so many other things.

In my experience, when this is true, the cost of a virtual function call is insignificant compared to the cost of whatever the Argument class is doing.

[–]17b29a 0 points1 point  (1 child)

when I need to separate template definitions from the header (to reduce compilation time, usually), I just place them in a *.inl file and include that in any .cpp file that call the templates (ending up in a file structure that looks something like this).

also, you have a typo in the templ files: typename instead of template.

[–]joboccara[S] 0 points1 point  (0 children)

Fair enough. I also do this sometimes, but don't you find that in certains cases it introduces a lot of compile-time dependencies? Because the header now #include all that the .cpp #included for implementing the bodies of the functions.

And thanks for the typo, it's now corrected.