I have a template library which uses CRTP
// Base configuration to be derived from.
struct TemplateBaseConfig {
static constexpr auto config_1 = true;
static constexpr auto config_2 = false;
static constexpr auto config_val_3 = 42;
};
template <typename Derived_, typename DerivedConfig_, typename... Widgets>
struct TemplateBaseClass {
std::tuple<Widgets...> widgets;
// Lots of functions and logic etc.
void function_1() {
if constexpr (DerivedConfig_::config_1) {
// do something
}
static_cast<Derived_*>(this)->function_2();
}
void function_2() { /*do something*/ }
};
Now the user uses TemplateBaseClass
as:
// Make a custom config.
struct DerivedConfig : TemplateBaseConfig {
static constexpr auto config_1 = false;
};
struct DerivedClass : TemplateBaseClass<DerivedClass, DerivedConfig, Widget1, Widget2> {
void function_2() { /* override base logic */ }
};
I write unittests for TemplateBaseClass
by inheriting a MockTemplateBaseClassInterface
.
template <typename TestingClass, typename TestingClassConfig, typename... W>
struct MockTemplateBaseClassInterface : TemplateBaseClass<TestingClass, TestingClassConfig, W> {
// Mock some internal functions which always need to be mocked.
MOCK(internal_function_1);
MOCK(internal_function_2);
// Some custom setup for unittests like override the log dir
void function_2() { Parent::function_2(); MakeLogDir(); } // Parent is this guy's base class.
}
To write unittests for a given DerivedConfig
, hence i create a derived class from MockTemplateBaseClassInterface
:
ClassToTestFunction1 : MockTemplateBaseClassInterface<ClassToTestFunction1, TestConfig1, MockWidget> {
// Write more mock functions if required.
MOCK(function3);
};
Then I use ClassToTestFunction1
in a fixture class to write unittests.
This works flawless to test anything for TemplateBaseClass
because i can given any configuration, write my own mocks when required.
Now if I have to expose this testing framework for the end user so that he can write tests for DerivedClass
, what should be the way forward?
I can ask the user to do something like:
struct DerivedClass :
#ifdef _DOING_TESTING_
MockTemplateBaseClassInterface<DerivedClass, DerivedConfig, Widget1, Widget2>
#else
TemplateBaseClass<DerivedClass, DerivedConfig, Widget1, Widget2>
#endif
{
void function_2() { /* override base logic */ }
};
But now the user cannot really mock TemplateBaseClass
's functions.
The user would want to test his function_2()
. But if function_2()
uses some function of TemplateBaseClass
he would want to mock it. I mean, that's the whole point of unittests right?