announce: type-parameterized tests available in trunk

306 views
Skip to first unread message

Zhanyong Wan (λx.x x)

unread,
Sep 9, 2008, 12:29:11 AM9/9/08
to Google C++ Testing Framework
Hi,

I'm glad to report that support for typed tests and type-parameterized tests has been committed to the svn.  If you check out the head revision, you can try it for yourself.  Many people contributed ideas, reviews, and feedback on earlier iterations of the implementation.  You know who they are if you have been following our discussions on this earlier. :-)

In case you don't know what type-parameterized tests are, they are abstract test patterns parameterized by a type.  They allow you to define a test suite once and instantiate it with different type arguments later.  I have update http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide with the usage.  Here's a copy:

Typed Tests

Suppose you have multiple implementations of the same interface and want to make sure that all of them satisfy some common requirements. Or, you may have defined several types that are supposed to conform to the same "concept" and you want to verify it. In both cases, you want the same test logic repeated for different types.

While you can write one TEST or TEST_F for each type you want to test (and you may even factor the test logic into a function template that you invoke from the TEST), it's tedious and doesn't scale: if you want m tests over n types, you'll end up writing m*n TESTs.

Typed tests allow you to repeat the same test logic over a list of types. You only need to write the test logic once, although you must know the type list when writing typed tests. Here's how you do it:

First, define a fixture class template. It should be parameterized by a type. Remember to derive it from testing::Test:

template <typename T>
class FooTest : public testing::Test {
 
public:
 
...
 
typedef std::list<T> List;
 
static T shared_;
  T value_
;
};

Next, associate a list of types with the test case, which will be repeated for each type in the list:

typedef testing::Types<char, int, unsigned int> MyTypes;
TYPED_TEST_CASE
(FooTest, MyTypes);

The typedef is necessary for the TYPED_TEST_CASE macro to parse correctly. Otherwise the compiler will think that each comma in the type list introduces a new macro argument.

Then, use TYPED_TEST() instead of TEST_F() to define a typed test for this test case. You can repeat this as many times as you want:

TYPED_TEST(FooTest, DoesBlah) {
 
// Inside a test, refer to the special name TypeParam to get the type
 
// parameter.  Since we are inside a derived class template, C++ requires
 
// us to visit the members of FooTest via 'this'.
 
TypeParam n = this->value_;

 
// To visit static members of the fixture, add the 'TestFixture::'
 
// prefix.
  n
+= TestFixture::shared_;

 
// To refer to typedefs in the fixture, add the 'typename TestFixture::'
 
// prefix.  The 'typename' is required to satisfy the compiler.
 
typename TestFixture::List values;
  values
.push_back(n);
 
...
}

TYPED_TEST
(FooTest, HasPropertyA) { ... }

You can see samples/sample6_unittest.cc for a complete example.

Availability: Linux, Windows (requires MSVC 8.0 or above), Mac; since version 1.1.0.

Type-Parameterized Tests

Type-parameterized tests are like typed tests, except that they don't require you to know the list of types ahead of time. Instead, you can define the test logic first and instantiate it with different type lists later. You can even instantiate it more than once in the same program.

If you are designing an interface or concept, you can define a suite of type-parameterized tests to verify properties that any valid implementation of the interface/concept should have. Then, the author of each implementation can just instantiate the test suite with his type to verify that it conforms to the requirements, without having to write similar tests repeatedly. Here's an example:

First, define a fixture class template, as we did with typed tests:

template <typename T>
class FooTest : public testing::Test {
 
...
};

Next, declare that you will define a type-parameterized test case:

TYPED_TEST_CASE_P(FooTest);

The _P suffix is for "parameterized" or "pattern", whichever you prefer to think.

Then, use TYPED_TEST_P() to define a type-parameterized test. You can repeat this as many times as you want:

TYPED_TEST_P(FooTest, DoesBlah) {
 
// Inside a test, refer to TypeParam to get the type parameter.
 
TypeParam n = 0;
 
...
}

TYPED_TEST_P
(FooTest, HasPropertyA) { ... }

Now the tricky part: you need to register all test patterns using the REGISTER_TYPED_TEST_CASE_P macro before you can instantiate them. The first argument of the macro is the test case name; the rest are the names of the tests in this test case:

REGISTER_TYPED_TEST_CASE_P(FooTest,
                           
DoesBlah, HasPropertyA);

Finally, you are free to instantiate the pattern with the types you want. If you put the above code in a header file, you can #include it in multiple C++ source files and instantiate it multiple times.

typedef testing::Types<char, int, unsigned int> MyTypes;
INSTANTIATE_TYPED_TEST_CASE_P
(My, FooTest, MyTypes);

To distinguish different instances of the pattern, the first argument to the INSTANTIATE_TYPED_TEST_CASE_P macro is a prefix that will be added to the actual test case name. Remember to pick unique prefixes for different instances.

In the special case where the type list contains only one type, you can write that type directly without testing::Types<...>, like this:

INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int);

You can see samples/sample6_unittest.cc for a complete example.

Availability: Linux, Windows (requires MSVC 8.0 or above), Mac; since version 1.1.0.

Please let me know if you have any questions.  Cheers,
--
Zhanyong

Zhanyong Wan (λx.x x)

unread,
Sep 12, 2008, 10:48:19 AM9/12/08
to David Oliver, Google C++ Testing Framework
2008/9/12 David Oliver <david...@google.com>:
> This looks nice, and fits in smoothly with "ergular" tests.
>
> BTW, is there a limit on the number of types in testing::Types<>?

The current implementation has a limit of 50 types at the moment, but
it can be easily extended, up to the limit imposed by the compiler.

There is also a limit of 50 type-parameterized tests per test case,
which can be extended in necessary too.

>
> 2008/9/8 Zhanyong Wan (λx.x x) <w...@google.com>

> --
> Cheers!
>
> David
>

--
Zhanyong

Reply all
Reply to author
Forward
0 new messages