У меня есть несколько модульных тестов, которые проверяют, равны ли определенные значения 0 или нет. В некоторых случаях они должны быть равны 0, а в некоторых нет.
Точно так же, как в следующем примере: testA ожидает, что valueA не будет содержать 0, тогда как testB ожидает, что valueB не будет.
Что я хочу сделать, так это каким-то образом обернуть часть проверки в функцию, поэтому вместо вызова EXPECT_NE/EXPECT_EQ для каждого члена я просто вызываю функцию, которая заботится о части проверки.
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);
}
Это то, что я придумал, но это слишком многословно. Хотите знать, есть ли лучший подход к этому?
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);
}





FieldsAre и структурированным переплетомС C++17 и последней версией GoogleTest (>= v1.12.0 ) вы можете просто использовать FieldsAre(), если Object допускает структурированное связывание (см. живой пример):
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)));
}
В противном случае (если ваш GoogleTest слишком старый или Object не допускает структурную привязку), вы можете написать простую функцию, подобную сопоставлению:
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));
}
и используйте его так же, как FieldsAre (живой пример):
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)));
}
Как отмечено в комментарии, ваш оригинальный Object является шаблоном, и в этом случае Field использовать нельзя. В этом случае вы можете написать правильное сопоставление клиентов следующим образом (живой пример):
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)));
}
@xyf Смотрите мою правку с MatchesValues(). Ваш Object не допускает структурированного связывания. Если доступ к переменным значительно сложнее, вы можете сделать MatchesValues() еще и полноценный сопоставитель (MATCHER_P2).
Что, если Object — это шаблон? Я попробовал, но не продвинулся достаточно далеко: godbolt.org/z/E7KnrrceK
@xyf Смотрите мой обновленный ответ, где я показываю пример с MATCHER_P2.
Это довольно аккуратно, но мой пример не совсем совпадает с тем, что я пробовал, потому что у меня было ощущение, что это не будет связано, но когда я попробовал 1-й подход в своем коде, я получаю
In template: cannot decompose class type 'Object<int>': both it and its base class 'ObjectBase<int>' have non-static data members