Home > OS >  Wrapping the EXPECT_NE, EXPECT_EQ into a validation function
Wrapping the EXPECT_NE, EXPECT_EQ into a validation function

Time:10-14

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)));
}
  • Related