I have a few unit tests that validate whether certain values equate to 0 or not. In some cases, they're supposed to be 0 and in some, they're not.
Just like the following: testA
expects valueA
to not contain 0 whereas testB
expects valueB
to not.
What I am looking to do is to somehow wrap the validation part in a function so instead of invoking EXPECT_NE
/EXPECT_EQ
for each member, I just invoke a function that takes care of the validation part.
TEST(UnitTest, testA)
{
Object object;
// do stuff that modified object's values
EXPECT_NE(object.valueA, 0);
EXPECT_EQ(object.valueB, 0);
}
TEST(UnitTest, testB)
{
Object object;
// do stuff that modified object's values
EXPECT_EQ(object.valueA, 0);
EXPECT_NE(object.valueB, 0);
}
This is what I came up with but it's a bit too verbose. Wondering if there's a better approach to it?
void Validate(Object* obj, bool valA, bool valB)
{
// verify valueB
if (valA)
{
EXPECT_EQ(object->valueA, 0);
}
else
{
EXPECT_NE(object->valueA, 0);
}
// verify valueB
if (valB)
{
EXPECT_EQ(object->valueB, 0);
}
else
{
EXPECT_NE(object->valueB, 0);
}
}
TEST(UnitTest, testA)
{
Object object;
// do stuff that modified object's values
Validate(&object, false, true);
}
TEST(UnitTest, testB)
{
Object object;
// do stuff that modified object's values
Validate(&object, true, false);
}
CodePudding user response:
With FieldsAre
and structured binding
With C 17 and a recent GoogleTest version (>= v1.12.0), you can simply use FieldsAre()
, in case Object
allows structured binding (see live example):
using ::testing::FieldsAre;
using ::testing::Eq;
using ::testing::Ne;
struct Object
{
int valueA;
int valueB;
};
TEST(UnitTest, testA)
{
Object object{42,0};
EXPECT_THAT(object, FieldsAre(Ne(0), Eq(0)));
}
TEST(UnitTest, testB)
{
Object object{0, 42};
EXPECT_THAT(object, FieldsAre(Eq(0), Ne(0)));
}
With a combination of matchers
Otherwise (if your GoogleTest is too old or Object
does not allow structured binding), you can write a simple matcher-like function:
using ::testing::Field;
using ::testing::AllOf;
template <class M1, class M2>
auto MatchesValues(M1 m1, M2 m2)
{
return AllOf(Field(&Object::valueA, m1), Field(&Object::valueB, m2));
}
and use it just like FieldsAre
(live example):
TEST(UnitTest, testA)
{
Object object{42,0};
EXPECT_THAT(object, MatchesValues(Ne(0), Eq(0)));
}
TEST(UnitTest, testB)
{
Object object{0, 42};
EXPECT_THAT(object, MatchesValues(Eq(0), Ne(0)));
}
With a custom matcher
As noted in the comment, your original Object
is a template, in which case Field
cannot be used. In this case you can write a proper customer matcher like so (live example):
template<typename T>
struct Object
{
int valueA;
int valueB;
T otherStuff;
Object(int a, int b) : valueA(a), valueB(b) {}
};
MATCHER_P2(MatchesValues, m1, m2, "")
{
return ExplainMatchResult(m1, arg.valueA, result_listener)
&& ExplainMatchResult(m2, arg.valueB, result_listener);
}
TEST(UnitTest, testA)
{
Object<int> object{42,0};
EXPECT_THAT(object, MatchesValues(Ne(0), Eq(0)));
}