GoogleTest test error print of enum class
Asked Answered
L

3

12

I'm using GoogleTest version 1.7.0 to test my C++ application. I have an enum defined as following

namespace MyNamespace {
    enum class MyEnum {
        MyEnumValue,
        MyEnumValue2
    }
}

GoogleTest incorrectly prints it's value, resulting in this error message when the test fails:

Value of: MyClass.MyMethodThatReturnsEnum()

Actual: 4-byte object

Expected: MyEnum::MyEnumValue

Which is: 4-byte object <02-00 00-00>

Removing the class keyword produces the correct error message with the real value of the enum. Is this a known behaviour/bug of GoogleTest? Is there a way to fix the problem?

MyClass my_class;

EXPECT_EQ(MyEnum::MyEnumValue, my_class.MyMethodThatReturnsEnum());
Lawrence answered 5/2, 2015 at 14:12 Comment(0)
S
4

When you define constants in enum class MyEnum you define them as objects of user-defined type MyEnum from which there is no implicit conversion to any integral type. That is the goal of enum class and it's not for Googletest to assume you want objects of type MyEnum automatically converted to an integral type for the purpose of inserting to an output stream, if you have chosen to make that type an enum class and not just an enum.

So what you observe isn't a Googletest error. It's just Googletest using its fallback representation of a user-defined object of some type T for which

std::ostream & operator<<(std::ostream &, T const &);

is undefined.

If you want to see the integral values of objects of type enum class MyEnum appear in Googletest's diagnostics for a test, you have at least two ways.

One way is to simply apply the test to the underlying integral values of the objects, as in:

main.cpp (1)

#include <gtest/gtest.h>
#include <type_traits>

enum class MyEnum {
    Value,
    Value2
};

auto as_integral(MyEnum me)
-> std::underlying_type<MyEnum>::type
{
    return static_cast<std::underlying_type<MyEnum>::type>(me);
}

TEST(foo,bar)
{
    EXPECT_EQ(as_integral(MyEnum::Value),as_integral(MyEnum::Value2));
}

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

Compile, link and run:

$ g++ -std=c++11 -Wall -Wextra -o gtester main.cpp -lgtest  -lpthread
$ ./gtester
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from foo
[ RUN      ] foo.bar
main.cpp:17: Failure
Expected equality of these values:
  as_integral(MyEnum::Value)
    Which is: 0
  as_integral(MyEnum::Value2)
    Which is: 1
[  FAILED  ] foo.bar (0 ms)
[----------] 1 test from foo (1 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (1 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] foo.bar

 1 FAILED TEST

Another - better - way is to provide Googletest with a definition of:

std::ostream & operator<<(std::ostream &, MyEnum const &);

that suits you. Then Googletest will use it in its diagnostics, as in:

main.cpp (2)

#include <gtest/gtest.h>
#include <type_traits>
#include <ostream>

enum class MyEnum {
    Value,
    Value2
};

auto as_integral(MyEnum me)
-> std::underlying_type<MyEnum>::type
{
    return static_cast<std::underlying_type<MyEnum>::type>(me);
}

std::ostream & operator<<(std::ostream & out, MyEnum me)
{
    return out << as_integral(me);
}

TEST(foo,bar)
{
    EXPECT_EQ(MyEnum::Value,MyEnum::Value2);
}

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

Compile, link and run:

$ g++ -std=c++11 -Wall -Wextra -o gtester main.cpp -lgtest  -lpthread
$ ./gtester
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from foo
[ RUN      ] foo.bar
main.cpp:23: Failure
Expected equality of these values:
  MyEnum::Value
    Which is: 0
  MyEnum::Value2
    Which is: 1
[  FAILED  ] foo.bar (0 ms)
[----------] 1 test from foo (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] foo.bar

 1 FAILED TEST
Samothrace answered 17/5, 2019 at 13:46 Comment(0)
C
2

The problem is probably that enum classes(officially named scoped enums) do not necessarily have the same size as enums(officially named unscoped enums).

The compiler decides which size is appropriate for your enum . Enum classes have a default size. This is why enum classes are easy to forward declare.

It looks like the test framework does not distinguish between the two.

Claiborne answered 5/2, 2015 at 14:50 Comment(7)
Thank you for your reply, I appreciate it. However, I still don't understand why GoogleTest prints the enum value as bytes. Shouldn't it be just a comparison? Can you elaborate a bit more?Lawrence
I'm not familiar with the framework but I would guess that it prints all objects this way. A 64 bit object would be printed as 8 bytes for example. You can correct me if I'm wrong.Claiborne
Furthermore the way it prints the message should have no bearing on the EXPECT_EQ() function.Claiborne
GoogleTest is able to print different C++ types, like std::string or std::vector objects with their real value using the PrintTo function. It seems to me that it's not able to understand the difference between scoped and normal enums, but I don't see the correlation between the binary size and this problemLawrence
The problem could arise if the comparison of values inside of EXPECT_EQU() detects and forbids downconversions so that it does not consider a char of value 8 equal to a long of value 8 for example.Claiborne
After some debugging into GoogleTest source code I found that it prints byte data because testing::internal::ImplicitlyConvertible<const MyEnum&, testing::internal::BiggestInt>::value returns false, but I have no knowledge to understand deeper the reason of thisLawrence
After extended research, I found that scoped enums can't be implicit casted to int, so this seems to be the reason that causes that like to returns false.Lawrence
T
1

You need to implement custom printing: https://github.com/google/googletest/blob/master/docs/advanced.md#teaching-googletest-how-to-print-your-values

I offer these in the mock implementations of the interfaces.

Tutelage answered 15/5, 2019 at 16:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.