GoogleTest PrintTo not getting called for a class
Asked Answered
T

3

18

I'm having a rather strange problem telling googletest to print a certain class the way I want using PrintTo.
The class is a very simple 2D point, it is in a namespace and the PrintTo function is in the same namespace. In fact, I have a derived class (a 3D point) which prints perfectly.

Here's some code for the tests and PrintTo functions (namespace name edited, but everything else is copied and pasted from the actual code):

// PrintTo Functions
namespace MyNamespace
{
    void PrintTo(const MyNamespace::CPunto2D& pto, ::std::ostream* os)
    {
        *os << "(";
        *os << pto.X();
        *os << ",";
        *os << pto.Y();
        *os << ")";
    }

    void PrintTo(const MyNamespace::CPunto3D& pto, ::std::ostream* os)
    {
        *os << "(";
        *os << pto.X();
        *os << ",";
        *os << pto.Y();
        *os << ",";
        *os << pto.m_Z;
        *os << ")";
    }
}

// Tests
TEST(TestPrintTo, TestPunto2D)
{
    MyNamespace::CPunto2D p1(1,1);
    MyNamespace::CPunto2D pRef(5,6);

    ASSERT_THAT(p1, Eq(pRef));
}

TEST(TestPrintTo, TestPunto3D)
{
    MyNamespace::CPunto3D pCentro(1,1,1);
    MyNamespace::CPunto3D pRef(5,6,7);

    ASSERT_THAT(pCentro, Eq(pRef));
}

// Output
[ RUN      ] TestPrintTo.TestPunto2D
.\TestPuntoEje.cpp(82): error: Value of: p1
Expected: is equal to 16-byte object <00-00 00-00 00-00 14-40 00-00 00-00 00-00 18-40>
  Actual: 16-byte object <00-00 00-00 00-00 F0-3F 00-00 00-00 00-00 F0-3F> (of type class MyNamespace::CPunto2D)
[  FAILED  ] TestPrintTo.TestPunto2D (1 ms)
[ RUN      ] TestPrintTo.TestPunto3D
.\TestPuntoEje.cpp(90): error: Value of: pCentro
Expected: is equal to (5,6,7)
  Actual: (1,1,1) (of type class MyNamespace::CPunto3D)
[  FAILED  ] TestPrintTo.TestPunto3D (0 ms)

I've tried to replicate the issue in a simple test project, but there it prints perfectly. The only difference I can think of between the test project and the real one is that in the real one the classes CPunto2D and CPunto3D are in a dll, with more classes, of course, and they depend on a library.

Any idea on why it's not picking the PrintTo function?

I'm using Visual Studio 2008 and googletest 1.7

Note: Even though the example uses GMock's ASSERT_THAT, I've tried it with ASSERT_EQ and it's the same.

UPDATE:

Here are the declarations of CPunto2D and CPunto3D. CLAS_DEC is just a macro for importing/exporting from the dll. I know there are like a million things wrong with the classes, like public members and such, so please don't point those thing out if it's not relevant to the problem at hand.

namespace MyNamespace
{
    class CLAS_DEC CPunto2D
    {
    public:
        double m_X;
        double X() const { return m_X; }
        void X(double val) { m_X = val; }
        double m_Y;
        double Y() const { return m_Y; }
        void Y(double val) { m_Y = val; }
        //Constructores/Destructores
        CPunto2D();
        CPunto2D(double X, double Y);
        CPunto2D(const CPunto2D& P);
        ~CPunto2D();

        CPunto2D& Set(double X, double Y);

        //Operadores
        CPunto2D& operator =(const CPunto2D& P);

        //Funciones extra
        double Distancia (const CPunto2D& P) const;  //Distancia a otro punto
    };
    bool CLAS_DEC operator==(const CPunto2D& lhs, const CPunto2D& rhs);
    bool CLAS_DEC operator!=(const CPunto2D& lhs, const CPunto2D& rhs);
}

namespace MyNamespace
{
    class CLAS_DEC CPunto3D : public CPunto2D
    {
    public:
        double m_Z;

        // Constructores/Destructores
        CPunto3D();
        CPunto3D(double X, double Y, double Z);
        CPunto3D(const CPunto3D& P);
        CPunto3D(const CPunto2D& P);
        ~CPunto3D();

        CPunto3D& Set(double X, double Y, double Z);

        // Operadores
        CPunto3D& operator =(const CPunto3D& P);
        bool operator==(const CPunto3D& P) const;
        bool operator!=(const CPunto3D& P) const;

        // Funciones Extra
        double Distancia (const CPunto3D& P) const;  //Distancia a otro punto
        double Distancia2D (const CPunto2D& P) const;  //Distancia en el plano a otro punto
    };
}
Tantalum answered 10/7, 2014 at 9:55 Comment(4)
Sorry: the classes are defined into a dll and you're using those from there but defining the print methods in your app?Ramachandra
Yes. Well, not in the final app. In the test cpp. Shouldn't it work?Tantalum
It might be a name resolution problem, I'm not sure of this to be honestRamachandra
I think you are on the right track. I've moved the PrintTo function to CPunto2D's header and it works now.Tantalum
P
19

Problem is that you break the One Definition Rule (ODR) of one of the gtest function (probably template ::testing::PrintToString<MyNamespace::CPunto2D>(const MyNamespace::CPunto2D&)).

In one TU where you use ASSERT_EQ, void PrintTo(const MyNamespace::CPunto2D& pto, ::std::ostream* os) is not declared, so ::testing::PrintToString<MyNamespace::CPunto2D> uses the default printer.

In an other TU where you use ASSERT_EQ, you have void PrintTo(const MyNamespace::CPunto2D& pto, ::std::ostream* os) declared (and potentially defined), so ::testing::PrintToString<MyNamespace::CPunto2D> uses a version using your custom PrintTo.

That is a second different definition of the same function.

You have to make sure that each TU which uses ASSERT_EQ see the declaration of your custom PrintTo (as in CPunto2D's header).

Plica answered 29/4, 2016 at 14:43 Comment(0)
J
1

This has caught me out, so I'm really glad to find this solution. My case was that MyType has an ostream& operator<<(), but it's defined in mytype_io.h rather than mytype.h. If any unit test cpp file uses MyType but doesn't include mytype_io.h then it will cause the default template implementation in gtest-printers.h to be instantiated.

So my type wasn't being printed correctly. The fix was to ensure that all unit tests that used MyType included mytype_io.h.

Jog answered 10/8, 2021 at 11:9 Comment(0)
W
0

Another scenario in which this can happen is if you define a mock object where the type in question is used as an argument to one of the mocked functions. If you define your printer after your mock is defined, then gmock somehow "locks in" the fallback printer instead of your defined printer.

Wilbertwilborn answered 16/11, 2023 at 21:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.