copy semantics of a mock object

1,994 views
Skip to first unread message

Bruce Trask

unread,
Apr 18, 2009, 12:22:15 AM4/18/09
to Google C++ Mocking Framework

Hello,

I have some code to get under test that may make a copy of a mock object that I pass in. It made me wonder what the official copy semantics of a google mock objects are. It appears that mocks and their copies share the same expectations, counts etc. (see example below).

Does it make sense to provide deep copy semantics such that copies don't share state that should possibly be unique to the particular instance(s) of the Mock class?

class TestMock
{
public:
MOCK_METHOD0(func, void());
// same as:
// ::testing::internal::Function<void()>::Result func()
// {
// typedef ::testing::internal::CompileAssert<(bool(::std::tr1::tuple_size< ::testing::internal::Function<void()>::ArgumentTuple>::value == 0))> this_method_does_not_take_0_arguments[bool(::std::tr1::tuple_size< ::testing::internal::Function<void()>::ArgumentTuple>::value == 0) ? 1 : -1]; gmock_func_16.SetOwnerAndName(this, "func"); return gmock_func_16.Invoke();
// }
// ::testing::MockSpec<void()>& gmock_func()
// {
// return gmock_func_16.RegisterOwner(this).With();
// }
// mutable ::testing::FunctionMocker<void()> gmock_func_16;
};

TEST(TestMock,Testit)
{
TestMock tm;

EXPECT_CALL(tm, func())
.Times(1);

TestMock tm1(tm);
tm1.func();
tm.func(); // will fail Times(1)
}

Regards,
Bruce

Zhanyong Wan (λx.x x)

unread,
Apr 18, 2009, 2:03:31 AM4/18/09
to Bruce Trask, Google C++ Mocking Framework
Hi Bruce,

Interesting idea. Currently Google Mock doesn't define a copy
semantics for mock objects, meaning that copying a mock object is
undefined behavior. We have never needed to copy a mock object
before, since mocks are usually a subclass of the type they are
mocking, and thus usually passed by pointer or reference instead of by
value.

If we support copying mock objects, we need to make sure the behavior
is both natural (i.e. no surprise to most people) and useful. I agree
that we shouldn't copy the expectations and call counts, which is kind
of natural to me. I don't know if other people consider it natural or
not.

We need to decide whether we should copy the default actions
(ON_CALLs). My initial thought is yes. I believe copying the default
actions but nothing else is a useful semantics, but I need more time
to think it through and make sure it's sound.

Is this semantics what you have in mind? I'm also curious on why you
need to copy your mocks and whether it's possible to rewrite the code
to avoid the copying (and whether the new code will be better or worse
than your current version).

Thanks,

--
Zhanyong

Bruce Trask

unread,
Apr 18, 2009, 2:44:58 AM4/18/09
to Zhanyong Wan (λx.x x), Google C++ Mocking Framework

Hi Zhanyong,


> Interesting idea. Currently Google Mock doesn't define a copy
> semantics for mock objects, meaning that copying a mock
> object is undefined behavior. We have never needed to copy a
> mock object before, since mocks are usually a subclass of the type they
> are mocking, and thus usually passed by pointer or reference
> instead of by value.

Makes sense.

> Is this semantics what you have in mind?

I too am still mulling it over and haven't quite made up my mind.

> I'm also curious on why you
> need to copy your mocks and whether it's possible to
> rewrite the code to avoid the copying (and whether the new code will be
> better or worse than your current version).

I have some legacy code that I am getting under test/control and it has some nasty static factory functions that I am trying to leave there for the moment while I get it under test. These statics should go away but they can't since they are part of a standard. They happen to do creation by way of the copy constructor.

Regards,
Bruce

Zhanyong Wan (λx.x x)

unread,
May 5, 2009, 6:36:21 PM5/5/09
to Bruce Trask, Google C++ Mocking Framework
2009/4/17 Zhanyong Wan (λx.x x) <w...@google.com>:

> Hi Bruce,
>
> Interesting idea.  Currently Google Mock doesn't define a copy
> semantics for mock objects, meaning that copying a mock object is
> undefined behavior.  We have never needed to copy a mock object
> before, since mocks are usually a subclass of the type they are
> mocking, and thus usually passed by pointer or reference instead of by
> value.
>
> If we support copying mock objects, we need to make sure the behavior
> is both natural (i.e. no surprise to most people) and useful.  I agree
> that we shouldn't copy the expectations and call counts, which is kind
> of natural to me.  I don't know if other people consider it natural or
> not.
>
> We need to decide whether we should copy the default actions
> (ON_CALLs).  My initial thought is yes.  I believe copying the default
> actions but nothing else is a useful semantics, but I need more time
> to think it through and make sure it's sound.

After thinking more about this, I decided that we should *not* copy
the default actions (ON_CALLs) of a mock object when copying it. The
reasons:

- Actions can be stateful. It's unclear what the user's intention is
when he copies a mock that has a stateful default action. For
example, suppose we have an action CountFrom(n) that returns n, n+1,
n+2, ..., on consecutive calls. If a mock has CountFrom(0) as a
default action and we copy the mock, should the new mock share the
same instance of CountFrom(0) with the original mock or have its own
instance? The decision will affect the two mocks' behavior, and we
have no way of knowing the user's intention. Plus, we don't have a
general way to clone an action unless we add a new (breaking)
requirement that all actions know how to clone themselves.
Therefore the deep copy semantics cannot even be implemented.

- An action can reference the mock object it is used in, as in:

ON_CALL(mock, Bar(_)).WillByDefault(Invoke(&mock, &MockFoo::BarHelper));

Here the action Inovoke(...) references 'mock'. If we copy this
action to a new mock object, the new action needs to reference the
new mock instead of the old one. Again, there's no general way to
do it as the implementation and semantics of an action is opaque to
Google Mock.

- Each mock method has a pointer that points to the mock object owning
it. This pointer is initialized when an ON_CALL or EXPECT_CALL on
this mock method is invoked, as that's the only time we can obtain
this information. If we support copying mock objects, we must make
mock methods copyable. However, we don't know the right value of
this pointer for the target mock method. Hence copying mock methods
is not implementable.

So, when we copy a mock object, none of its state can really be
copied. All we can reasonably do for the general case is to return a
fresh mock object whose state is empty (no ON_CALLs, no EXPECT_CALLs,
and no call counts). This "copy" semantics isn't what people usually
expect from a copy operation. Calling it "copy" is misleading and
easily leads to user errors. Therefore, now I think we should *not*
support copying mocks at all.

Here's my proposal:

- We should make mock objects uncopyable such that you get a compiler
error when trying to copy a mock or assigning to it. This is good
as it catches user errors.

- If you really want to be able to copy a mock, it's up to you to
implement an appropriate copy operation. For example, if you are
fine with getting a fresh mock object as the result of this
"copying", you can write:

class MockFoo : public Foo {
public:
// The copy constructor does nothing.
MockFoo(const MockFoo& foo) {}

MOCK_METHOD1(Bar, void(int n));
...
};

Thoughts? Thanks,

--
Zhanyong

Bruce Trask

unread,
May 6, 2009, 6:16:08 PM5/6/09
to Zhanyong Wan (λx.x x), Google C++ Mocking Framework

Hi Zhanyong,

All great points. Agreed.

> Here's my proposal:
>
> - We should make mock objects uncopyable such that you get
> a compiler
>   error when trying to copy a mock or assigning to
> it.  This is good
>   as it catches user errors.
>
> - If you really want to be able to copy a mock, it's up to
> you to
>   implement an appropriate copy operation.  For
> example, if you are
>   fine with getting a fresh mock object as the result
> of this
>   "copying", you can write:
>
>     class MockFoo : public Foo {
>      public:
>       // The copy constructor does nothing.
>       MockFoo(const MockFoo& foo) {}
>
>       MOCK_METHOD1(Bar, void(int n));
>       ...
>     };
>
> Thoughts?  Thanks,

Sounds good. What mechanism did you want to use to make the mock not copyable? A private unimplemented copy constructor that gets put there in the macros somehow? or something else?

How would one undo that if they wanted to copy it?

Also, now that I am thinking about it, in my original case, I should have just wrapped my mock in a mock wrapper that reference counted the inner one without copying it and that would have sufficed to get around the copying done by my unit under test.

Regards,
Bruce

Zhanyong Wan (λx.x x)

unread,
May 6, 2009, 9:01:59 PM5/6/09
to Bruce Trask, Google C++ Mocking Framework
Hi Bruce,

2009/5/6 Bruce Trask <bruce...@mdesystems.com>:


> Sounds good.   What mechanism did you want to use to make the mock not copyable?  A private unimplemented copy constructor that gets put there in the macros somehow? or something else?

A mock class consists a bunch of mock methods, where each mock method
is implemented using a FunctionMocker object to record its state. In
other words, MOCK_METHODn(...) expands to (among other things) a
member variable of type FunctionMocker. Therefore we only need to
make FunctionMocker uncopyable by giving it a private unimplemented
copy ctor. After this change, all mock classes will automatically
become uncopyable (since they have uncopyable member variables). The
user doesn't need to change his mock class definition in any way.

> How would one undo that if they wanted to copy it?

They'll need to define their own copy ctor for the mock class, as in
the example I gave.

> Also, now that I am thinking about it, in my original case, I should have just wrapped my mock in a mock wrapper that reference counted the inner one without copying it and that would have sufficed to get around the copying done by my unit under test.

Sounds good.

>
> Regards,
> Bruce
>
>
>
>

--
Zhanyong

Zhanyong Wan (λx.x x)

unread,
May 14, 2009, 5:44:58 PM5/14/09
to Bruce Trask, Google C++ Mocking Framework
We just made mock objects uncopyable in revision 154. Cheers,

2009/5/6 Zhanyong Wan (λx.x x) <w...@google.com>:

--
Zhanyong

Reply all
Reply to author
Forward
0 new messages