*** has not been declared (though it is)
Solution 1:
You have an architectural problem, you are abstracting the different types of streams in a bad spot, that cannot easily be untangled. The mock interface is too tightly entwined with the code under test. You can hack around this, but you are better off stopping and thinking through exactly what you want to test.
Sort of side note:
StreamImpl
is a bad idea.
StreamImpl& operator<<(T &&out) noexcept;
is non-standard and part of how you got into this problem. If you use the standard
std::ostream & operator<<(std::ostream & strm, T &&out) noexcept;
and turn StreamImpl
into a child of std::ostream
you can pass in a re-write of MockStreamImpl
that is ALSO a child of std::ostream
and completely hide the specialization of the stream inside the stream rather than the function writing to the stream.
Side note to the side note: Templating operator<<
isn't a very good idea either. Most types don't have similar-enough rules to be printable by a generic function. This leads to goofy output or confused error messages when the <<
template tries to invoke itself because it's still the best match.
What went wrong:
If we follow the includes and perform the substitutions we can see what the compiler sees:
#include "MockStreamImpl.hpp"
namespace fixture {
using ::mock::MockStreamImpl;
using ::utils::Dispatcher;
using ::testing::Test;
class UtStream : public Test {
// Class for setting up mocks and common test data which is used as fixture in tests
StreamImpl kDefaultStream_{};
}
} // namespace fixture
leads to
#include "StreamImpl.hpp"
namespace mock {
using ::StreamImpl;
class MockStreamImpl {
MOCK_METHOD(...); // Gmock methods
};
} // namespace mock
namespace fixture {
using ::mock::MockStreamImpl;
using ::utils::Dispatcher;
using ::testing::Test;
class UtStream : public Test {
// Class for setting up mocks and common test data which is used as fixture in tests
StreamImpl kDefaultStream_{};
}
} // namespace fixture
and then to
class StreamImpl {
...
template <typename Type>
StreamImpl &operator<<(T &&out) noexcept;
...
};
#include "StreamTemplateImpl.cpp"
namespace mock {
using ::StreamImpl;
class MockStreamImpl {
MOCK_METHOD(...); // Gmock methods
};
} // namespace mock
namespace fixture {
using ::mock::MockStreamImpl;
using ::utils::Dispatcher;
using ::testing::Test;
class UtStream : public Test {
// Class for setting up mocks and common test data which is used as fixture in tests
StreamImpl kDefaultStream_{};
}
} // namespace fixture
and then, yawn, to
class StreamImpl {
...
template <typename Type>
StreamImpl &operator<<(T &&out) noexcept;
...
};
// Templates implementation needed for test purposes
#include "StreamImpl.hpp"
#include "MockStreamImpl.hpp"
using ::utils::Dispatcher;
using ::mock::MockStreamImpl; // Error here
template <typename Type>
StreamImpl &operator<<(T &&out) noexcept
{
return Dispatcher<MockStreamImpl>::Instance().Get()->OutOperator(out);
}
namespace mock {
using ::StreamImpl;
class MockStreamImpl {
MOCK_METHOD(...); // Gmock methods
};
} // namespace mock
namespace fixture {
using ::mock::MockStreamImpl;
using ::utils::Dispatcher;
using ::testing::Test;
class UtStream : public Test {
// Class for setting up mocks and common test data which is used as fixture in tests
StreamImpl kDefaultStream_{};
}
} // namespace fixture
and since StreamImpl.hpp and MockStreamImpl.hpp are protected by include guards we get
class StreamImpl {
...
template <typename Type>
StreamImpl &operator<<(T &&out) noexcept;
...
};
// Templates implementation needed for test purposes
using ::utils::Dispatcher;
using ::mock::MockStreamImpl; // Error here
template <typename Type>
StreamImpl &operator<<(T &&out) noexcept
{
return Dispatcher<MockStreamImpl>::Instance().Get()->OutOperator(out);
}
namespace mock { //because mock isn't found by the compiler until here
using ::StreamImpl;
class MockStreamImpl {
MOCK_METHOD(...); // Gmock methods
};
} // namespace mock
namespace fixture {
using ::mock::MockStreamImpl;
using ::utils::Dispatcher;
using ::testing::Test;
class UtStream : public Test {
// Class for setting up mocks and common test data which is used as fixture in tests
StreamImpl kDefaultStream_{};
}
} // namespace fixture
where we can see that MockStreamImpl
is required before it is defined.