Search code examples
c++unit-testingcatch2

How to add description to a test case using overloaded equality(==) operator?


TEST_CASE("Functions of Square")
{
    std::array<Point, 4> a_Vertices{Point(2, 2), Point(2, 4), Point(4, 2), Point(4, 4)};
    Square a_Square(a_Vertices, true);

    SECTION("Square is initialized properly")
    {
        REQUIRE(a_Square.get_Vertex_0() == Point(2, 1));
    }
}

Using catch2 unit test framework, I want to override catch2 describe method so that it would print out my type instead of {?} == {?}

I've also tried doing it via matcher method

class Point_Matcher : public Catch::MatcherBase<Point>
{
private:
    Point p_;

public:
    Point_Matcher(const Point &a_Point)
        : p_(a_Point) {}

    bool match(Point const &a_Point) const override
    {
        return a_Point == p_;
    }

    virtual std::string describe() const override
    {
        std::ostringstream oss;
        oss << "not the same as Point (" << p_.get_X() << ", " << p_.get_Y() << ")";
        return oss.str();
    }
};

inline Point_Matcher is_Same(const Point &a_Point)
{
    return Point_Matcher(a_Point);
}

TEST_CASE("Functions of Square")
{
    std::array<Point, 4> a_Vertices{Point(2, 2), Point(2, 4), Point(4, 2), Point(4, 4)};
    Square a_Square(a_Vertices, true);

    SECTION("Square is initialized properly")
    {
        REQUIRE_THAT(a_Square.get_Vertex_0(), is_Same(Point(2, 1)));
    }
}

however, it would only show the object that's specified as the test and wouldn't show the object being tested. Output will be {?} not the same as Point(2, 1)

in https://github.com/catchorg/Catch2/blob/master/docs/tostring.md#top The suggested way of doing it is to override the operator<< of std::ostream, however, i don't know what i'm supposed to do after i overloaded the operator.

Thank you in advance for the answer

Edit: The overload for operator<< for Point Object is as follows

std::ostream &operator<<(std::ostream &os, const Point &p)
{
    os << "Point (" << p.get_X() << ", " << p.get_Y();
    return os;
}

In other words, the output, that i'm aiming for is in this case in particular Point(x, y) not the same as Point(x,y)


Solution

  • There are two approaches that are commonly used. Both are described in documentation you have mentioned.

    1. Operator << overload for std::ostream with restriction that:

    You should put this function in the same namespace as your type, or the global namespace, and have it declared before including Catch's header.

    So, your operator overload is ok, just put it before #include "catch.hpp".

    #define CATCH_CONFIG_MAIN
    #include <array>
    #include "Point.h"
    #include "Square.h"    
    
    std::ostream &operator<<(std::ostream &os, Point const& p)
    {
        os << "Point (" << p.get_X() << ", " << p.get_Y() << ")";
        return os;
    }
    
    #include "catch.hpp"
    
    TEST_CASE("Functions of Square")
    {
        std::array<Point, 4> a_Vertices{Point(2, 2), Point(2, 4), Point(4, 2), Point(4, 4)};
        Square a_Square(a_Vertices, true);
    
        SECTION("Square is initialized properly")
        {
            REQUIRE_THAT(a_Square.get_Vertex_0(), is_Same(Point(2, 1)));
        }
    }
    

    2. Catch::StringMaker specialisation with additional stringstream usage to format your output correctly.

    #define CATCH_CONFIG_MAIN
    #include <array>
    #include <sstream>
    #include "Point.h"
    #include "Square.h"
    #include "catch.hpp"
    
    namespace Catch
    {
    template <> struct StringMaker<Point>
    {
        static std::string convert(Point const& p)
        {
            std::stringstream buf;
            buf << "Point (" << p.get_X() << ", " << p.get_Y() << ")";
            return << buf.str();
        }
    };
    }
    
    TEST_CASE("Functions of Square")
    {
        std::array<Point, 4> a_Vertices{Point(2, 2), Point(2, 4), Point(4, 2), Point(4, 4)};
        Square a_Square(a_Vertices, true);
    
        SECTION("Square is initialized properly")
        {
            REQUIRE_THAT(a_Square.get_Vertex_0(), is_Same(Point(2, 1)));
        }
    }    
    

    This approach avoids global namespace pollution and let you mask any other previously defined << operator. StringMaker specialization will be used at first.