Parameterized tests with container from test fixture

407 views
Skip to first unread message

Alex Shaver

unread,
Feb 18, 2015, 2:26:47 PM2/18/15
to googletes...@googlegroups.com
class A{
public:
  A() : Alpha(0), Aleph(0){}

 
void setAlpha(const int a) { Alpha = a; }
 
void setAleph(const int a) { Aleph = a; }

 
int alpha() const { return Alpha; }
 
int aleph() const { return Aleph; }

private:
 
int Alpha, Aleph;
}

Suppose I have the above class with setters and getters (a simplified case of what I'm working with at the moment)

Suppose I wish to unit test these. 

I could write 
TEST(ATests, Alpha){
  A alex
;
  alex
.setAlpha(1234);

  EXPECT_
EQ(1234, alex.alpha)
}

TEST
(ATests, Aleph){
  A alex
;
  alex
.setAleph(1234);

  EXPECT_
EQ(1234, alex.aleph)
}

But if I have many member variables, these tests can become tedious. Especially if other things occur, like Qt Signal emission when Alpha or Aleph changes, incurring new tests around the changes, or accessing the value through the Qt Property system instead of the standard set/get. 

what I would like to be able to do is something like the following: (note, this uses c++11, but I think it should be possible without that standard as well, but more complicated to set up)

#include <list>
#include <tuple>
#include <functional>

using std::list;
using std::tuple;
using std::function;

typedef tuple<function<void(int)>, function<int()> > ATestTuple_t;
typedef list<ATestTuple_t> ATestList_t;

using std::placeholders::_1;
class ATestData {
public:
 
ATestData(A* testObject){
    testDataList
.emplaceLast(std::bind(&A::setAlpha, testObject, _1),
                             std
::bind(&A::alpha, testObject, _1));

    testDataList
.emplaceLast(std::bind(&A::setAleph, testObject, _1),
                             std
::bind(&A::aleph, testObject, _1));
 
}

 
ATestList_t testDataList;
}

using ::testing::Test;

class ATests : public Test{
protected:
 
void SetUp(){
   
TestData = new ATestData(&TestObject);
 
}
 
void TearDown(){
   
delete TestData; TestData = nullptr;
 
}

  A
TestObject;
 
ATestData* TestData;
 
const int ExpectedValue = 1234;
}

class A_ParamTests : public ATests, public ::testing::WithParamInterface<ATestTuple_T>{
protected:
 
void SetUp(){
   
ATests::SetUp();
   
ATestTuple_T testTuple = GetParam();
    setter
= std::get<0>(testTuple);
    getter
= std::get<1>(testTuple);
 
}

 
function<void(int)> setter;
 
function<int(void)> getter;
};

TEST_P
(A_ParamTests, tests){
  setter
(ExpectedValue);

  EXPECT_EQ
(ExpectedValue, getter());
}

INSTANTIATE_TEST_CASE_P
(SetGetParamTests, A_ParamTests, ::testing::ValuesIn(TestData.testDataList))


I know it's a lot of code, but hopefully you can see what I'm getting at. If I can have functions in my test parameters, then I can rapidly test a whole lot of functions that do more or less the same thing, but have varying, correlated, names. 

--------------------------

For instance, in my real world example, I have a class inheriting from QObject with QProperty values, and notification signals when the value changes. So, for each property there are 5 tests:
  1. use setter, expect change signal emitted
  2. use setter twice with same value, expect change signal emitted only once
  3. use setProperty("propertyName"), expect change signal
  4. use getter to confirm it matches set value
  5. use property("propertyName") to confirm matches set value
One of my classes has 7 properties. That's 7 x 5 tests whose only real variation is the specific name of the function to use to set the value or get the value. 

If I had each of those tests parameterized on the function rather than parameters to be passed to a function, That would simplify the test code greatly.

Samuel Benzaquen

unread,
Feb 18, 2015, 4:47:06 PM2/18/15
to Alex Shaver, Google C++ Testing Framework
I don't understand. You are parametizing on functions (instances of std::function<> to be specific).
The only problem I see is that even though the setter, getter and name are related, they are separate entities that you need to group.
One solution is to create a small "descriptor" struct for each property. Something like this:

struct PDesc {
  int (A::*getter)() const;
  void (A::*setter)(int);
  const char* name;
};

// Then instantiation is simpler:

class ATests : public ::testing::Test, ::testing::WithParamInterface<PDesc>{
protected:
  void SetUp(){
    desc = GetParam();
  }
  PDesc desc;
};


TEST_P(ATests, GetSet){
  A a;
  EXPECT_NE(ExpectedValue, (a.*desc.getter)());
  (a.*desc.setter)(ExpectedValue);
  EXPECT_EQ(ExpectedValue, (a.*desc.getter)());
}

static const PDesc AProps[] = {
  { &A::alpha, &A::setAlpha, "alpha" },
  { &A::aleph, &A::setAleph, "aleph" }
};
INSTANTIATE_TEST_CASE_P(SetGetParamTests, ATests, ::testing::ValuesIn(AProps))

--

---
You received this message because you are subscribed to the Google Groups "Google C++ Testing Framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to googletestframe...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Alex Shaver

unread,
Feb 19, 2015, 11:25:12 AM2/19/15
to googletes...@googlegroups.com, alexs...@gmail.com
Ah thanks, you gathered my intentions correctly, I think. This simpler implementation is much better at doing what I want it to do. 
To unsubscribe from this group and stop receiving emails from it, send an email to googletestframework+unsub...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages