Search code examples
c++unit-testingtestinggooglemock

How do you create a templated custom matcher with gmock


I wanted to use WhenDynamicCastTo<T>(m) from: https://google.github.io/googletest/reference/matchers.html#pointer-matchers

But the thing I want to dynamic cast is in a shared_ptr and WhenDynamicCastTo doesn't work with a smart pointer.

I tried to make my own version that works with smart pointers and I'm not sure how to make a templated matcher, I tried this but it doesn't work:

template <typename To>
MATCHER_P(WhenDynamicPointerCastTo<To>, matcher, "")
{
    // Use std::dynamic_pointer_cast here
}

Doing this does work but I'd like to hide it behind a single matcher:


template <typename To>
std::shared_ptr<const To> dynamic_smart_pointer_cast(
    const std::shared_ptr<const parent>& p)
{
    return std::dynamic_pointer_cast<const To>(p);
}

EXPECT_CALL(*mock_, func(
    ::testing::ResultOf(
        dynamic_smart_pointer_cast<child>,
        ::testing::Pointee(...))));

Solution

  • I wanted to add an answer as I found a few ways to do it in the end.

    I was able to actually use the gmock built in WhenDynamicCastTo.

    The function I was mocking was like:

    void func(const std::shared_ptr<interface_type>& i);
    

    When I tried to use WhenDynamicCastTo like this it was complaining about the type I was casting to being abstract (It had a pure virtual function as well):

    EXPECT_CALL(*mock_, func(
        ::testing::Pointee(::testing::WhenDynamicCastTo<impl>(<do_matching>))));
    

    To make this work with gmocks built in WhenDynamicCastTo I had to change the template parameter to a reference:

    EXPECT_CALL(*mock_, func(
        ::testing::Pointee(::testing::WhenDynamicCastTo<const impl&>(<do_matching>))));
    

    I was able to make Erik Man's suggesting work with some changes to the template parameters and thought I'd add it incase someone wanted to do something like that:

    template <typename To>
    class WhenDynamicPointerCastToMatcher
    {
    public:
        explicit WhenDynamicPointerCastToMatcher(
            const ::testing::Matcher<std::shared_ptr<To> >& matcher) :
                matcher_(matcher)
        {
        }
    
        void DescribeTo(::std::ostream* os) const
        {
            GetCastTypeDescription(os);
            matcher_.DescribeTo(os);
        }
    
        void DescribeNegationTo(::std::ostream* os) const
        {
            GetCastTypeDescription(os);
            matcher_.DescribeNegationTo(os);
        }
    
        template <typename From>
        bool MatchAndExplain(From from, ::testing::MatchResultListener*) const
        {
            const std::shared_ptr<To> to{std::dynamic_pointer_cast<To>(from)};
    
            // To-Do: describe failures to the listener
            return matcher_.Matches(to);
        }
    
    private:
        static void GetCastTypeDescription(::std::ostream* os)
        {
            *os << "when dynamic_pointer_cast";
        }
    
      const ::testing::Matcher<std::shared_ptr<To> > matcher_;
    };
    
    template <typename To>
    inline ::testing::PolymorphicMatcher<WhenDynamicPointerCastToMatcher<To> > WhenDynamicPointerCastTo(
        const ::testing::Matcher<std::shared_ptr<To> >& inner_matcher)
    {
        return ::testing::MakePolymorphicMatcher(
            WhenDynamicPointerCastToMatcher<To>(inner_matcher));
    }
    
    EXPECT_CALL(*mock_, func(
        WhenDynamicPointerCastTo<const impl>(::testing::Pointee(<do_matching>))));