Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strategy to unit test move/copy constructors?

I want to write unit tests to test move/copy constructors/assigments of some classes I am writing. I want to make sure that the resources are handled appropriately, that a move ctor is called when I expect it to be called instead of a copy ctor, and vice-versa.

The problem is that I don't want to mess with the class code in order to test this. So, is there a way to know from the test code outside the class, when the move or the copy ctors/assignments were called?

What's a general strategy to unit-test copy/move ctors/assignments?

PD: I am using Catch unit-testing framework, so please provide an answer that can be implemented in Catch.

like image 727
becko Avatar asked Jul 29 '15 14:07

becko


People also ask

What is the difference between copy constructor and move constructor?

Move constructor moves the resources in the heap, i.e., unlike copy constructors which copy the data of the existing object and assigning it to the new object move constructor just makes the pointer of the declared object to point to the data of temporary object and nulls out the pointer of the temporary objects.

Are move constructors automatically generated?

If a copy constructor, copy-assignment operator, move constructor, move-assignment operator, or destructor is explicitly declared, then: No move constructor is automatically generated. No move-assignment operator is automatically generated.

Why do we need move constructors?

A move constructor enables the resources owned by an rvalue object to be moved into an lvalue without copying. For more information about move semantics, see Rvalue Reference Declarator: &&. This topic builds upon the following C++ class, MemoryBlock , which manages a memory buffer.

Why and when do you need copy constructors Is there any situation where you want to only implement a copy constructor or an assignment operator but not both?

A user-defined copy constructor is generally needed when an object owns pointers or non-shareable references, such as to a file, in which case a destructor and an assignment operator should also be written (see Rule of three).


1 Answers

I have never used Catch so can't provide a working example.

However, I unit test classes with complicated copy/move constructors (e.g. involving deep copies, std::unique_ptr etc) by testing the class methods on the moved/copied objects.

For example, if I have a class as follows:

// constructor
Foo::Foo(int i) : privateMember(i) {};

// some function that operates on this private member
int Foo::bar() { return privateMember + 5 };

I would have a test for the bar() method, but then duplicate it for the move, copy etc constructors. You can easily end up with three or four times the original tests.

I don't expose member variables. If you do then you may be able to use "is same" test functions (assuming Catch supports this). For example, making sure a deep copy creates a unique copy (i.e. doesn't point back to the original object).

Update

You seem to be more concerned that the correct move or copy constructor/assignment is called. If you simply want "move assignment" or some other identifier provided I don't know what to suggest.

When I test copy, I make sure I copy the object in my unit test, then call functions that depend on shallow/deep copies of member variables. This is the same for when I test move. In some scenarios you can run tests on the original object (e.g. test that a vector instance variable now has a size of zero after the move).

I force a move constructor as follows:

Foo foo{};
Foo bar(std::move(foo));

I force a move assignment as follows:

Foo foo{};
Foo bar{};
bar = std::move(foo);

I force a copy constructor as follows:

Foo foo{};
Foo bar(foo);

I force a copy assignment as follows:

Foo foo{};
Foo bar{};
bar = foo;

If you really need this you could possibly inherit from some debug virtual class that exposes a enum class field. In your debug build the field gets populated with an appropriate enum value (MoveConstructor). This field is exposed so you can test it after a copy/move. Make sure that you populate it in your copy/move constructors/assignment methods.

like image 124
Class Skeleton Avatar answered Oct 31 '22 10:10

Class Skeleton