Обертывание EXPECT_NE, EXPECT_EQ в функцию проверки

У меня есть несколько модульных тестов, которые проверяют, равны ли определенные значения 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);
}
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
108
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

С 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)));
}

Это довольно аккуратно, но мой пример не совсем совпадает с тем, что я пробовал, потому что у меня было ощущение, что это не будет связано, но когда я попробовал 1-й подход в своем коде, я получаю In template: cannot decompose class type 'Object<int>': both it and its base class 'ObjectBase<int>' have non-static data members

xyf 12.10.2022 21:28

@xyf Смотрите мою правку с MatchesValues(). Ваш Object не допускает структурированного связывания. Если доступ к переменным значительно сложнее, вы можете сделать MatchesValues() еще и полноценный сопоставитель (MATCHER_P2).

Sedenion 12.10.2022 21:31

Что, если Object — это шаблон? Я попробовал, но не продвинулся достаточно далеко: godbolt.org/z/E7KnrrceK

xyf 12.10.2022 21:40

@xyf Смотрите мой обновленный ответ, где я показываю пример с MATCHER_P2.

Sedenion 13.10.2022 08:24

Другие вопросы по теме