As a fan of regular types and value semantics, I'm keen on classes becoming more regular and being non-polymorphic. As a fan of non-throwing operations, I'm keen on operations being noexcept
. I also appreciate confirmation of what special member functions are actually being generated by the compiler. And I'd like to track these things along with code these are associated with.
C++ offers the <type_traits>
header file which offers classes for checking qualities like these and I've found myself enjoying making use of them in unit test code like the following:
TEST(MyClass, Traits)
{
EXPECT_TRUE(std::is_constructible< MyClass >::value);
EXPECT_TRUE(std::is_nothrow_constructible< MyClass >::value);
EXPECT_FALSE(std::is_trivially_constructible< MyClass >::value);
EXPECT_TRUE(std::is_copy_constructible< MyClass >::value);
EXPECT_TRUE(std::is_nothrow_copy_constructible< MyClass >::value);
EXPECT_FALSE(std::is_trivially_copy_constructible< MyClass >::value);
EXPECT_TRUE(std::is_move_constructible< MyClass >::value);
EXPECT_TRUE(std::is_nothrow_move_constructible< MyClass >::value);
EXPECT_FALSE(std::is_trivially_move_constructible< MyClass >::value);
EXPECT_TRUE(std::is_copy_assignable< MyClass >::value);
EXPECT_TRUE(std::is_nothrow_copy_assignable< MyClass >::value);
EXPECT_FALSE(std::is_trivially_copy_assignable< MyClass >::value);
EXPECT_TRUE(std::is_move_assignable< MyClass >::value);
EXPECT_TRUE(std::is_nothrow_move_assignable< MyClass >::value);
EXPECT_FALSE(std::is_trivially_move_assignable< MyClass >::value);
EXPECT_TRUE(std::is_destructible< MyClass >::value);
EXPECT_TRUE(std::is_nothrow_destructible< MyClass >::value);
EXPECT_FALSE(std::is_trivially_destructible< MyClass >::value);
EXPECT_TRUE(!std::is_polymorphic< MyClass >::value);
}
As unit test code that's also going into my source code revision system, this has the advantage of tracking these qualities along with the code being committed for MyClass
. I also like getting a gamification kick of out seeing classes take on more of these qualities - which I do using code like this.
I wonder however: are there other ways to achieve this that perhaps have more benefits without as many pitfalls?
Here's some concerns that I have about this technique:
- Are these acceptable as unit tests? They are to me but are they to others? While the definition of unit testing which Wikipedia provides seems subjective enough for this to qualify, I'm concerned here how widely others would agree.
- Would it be better to track these qualities elsewhere like in the class's code itself by using
static_assert
for example? This is different from the last concern in now focusing on whether their's a better place (not whether other software developers would accept this as unit testing). Tracking these qualities in the class's code viastatic_assert
instead of in unit test code like shown, seems like an elevation of these qualities importance. For more performance critical classes that direction makes some sense. I also recognize a perspective that these should instead be lowered in importance to perhaps just being warnings or messages (instead of test failures or build failures). - Lack of scalability. Is it more boilerplate code than it needs to be? It's an easy copy and paste operation to create tests like this but it doesn't lend itself to global updates. I'd prefer to abstract this more if possible so there was a single place it could be updated from. I've considered converting this into a template function that combines these checks and just gets the type as its template parameter. How would that track the true and false values however? It could generate an output report but that seems cumbersome to parse back into
EXPECT_*
contexts with local line numbers. A macro function meanwhile would be evaluated in the unit test's context but a multi-statement macro function still shows up as all on one source code line: the line where the macro is called from. I've also considered combining the normal,nothrow
, andtrivially
forms of these checks, into single line checks taking a parameter to indicate which of these should apply in the positive. I haven't seen anything more satisfying yet than the shown pattern however.