google / google/googletest

Add support for movable mocks

Open
#4,059 4 comments 17 reactions 1 assignee Claimed by @asoffer View on GitHub
enhancement
Dominant language
C++
Stars
39.6k
Forks
10.9k
Avg merge
6d 13h
Merged PRs (30d)
1

Description

**Does the feature exist in the most recent commit?**

No

**Why do we need this feature?**

With the introduction of concepts and CTAD in C++, together with the growing realization of just how awesome value semantics are, dependency injection using templated, and concept conforming values, are becoming much more prevalent. I would go as far as to say it's becoming the new normal, unless you really need runtime polymorphism. It's a shame that there is no easy way to test this style of code using gtest/gmock.

I realize that this is a design issue. Providing mocking of copyable objects requires clarity on the mocking semantics. Perhaps you could provide a way of opting in to this by providing some kind of configuration of the mock object when it's defined?

This suggestion was also added to https://github.com/google/googletest/issues/3734 after it had been closed. But since there was no further activity there I assumed that the closed ticket was the wrong place for continuing this discussion.

**Describe the proposal**

This would be my suggested semantics:

Make the mock movable. Since there is only ever one valid mock object, that's the one that should record actions and do validation. The moved from husks should not record any further actions and not do any validation.

Make the mock copyable and treat all copies like shared_ptr to one and the same mock. I.e. all objects record actions but only the last one standing does validation. There will be some overhead to guarantee thread safety, but if this behavior is opt-in I'm sure most users would be fine with it.
https://github.com/google/googletest/issues/3734
Here is an example of code that would be awesome if it was possible to get working (without having to resort to pointers and dynamic memory allocations). Here is the same thing on godbolt: https://godbolt.org/z/G83Eqcvv9

```c++
#include
#include

template
concept Engine = requires(T e) {
{ e.start() } -> std::convertible_to;
};

template
struct Car {
explicit Car(const TEngine& engine) : engine(engine) {}
explicit Car(TEngine&& engine) : engine(std::move(engine)) {}

bool start() { return engine.start(); }

private:
TEngine engine;
};

struct ElectricEngine {
bool start() { return true; }
};
static_assert(Engine);

// Test code

#include "gmock/gmock.h"
#include "gtest/gtest.h"
using ::testing::Return;

struct MockEngine {
public:
MOCK_METHOD(bool, start, (), ());
};
static_assert(Engine);

TEST(car_tests, engine_start) {
// This is similar to the production code
Car electric_car{ElectricEngine{}};
ASSERT_TRUE(electric_car.start());

// This would be nice to be able to do but doesn't work without this feature
MockEngine mock_engine{};
EXPECT_CALL(mock_engine, start()).Times(1).WillOnce(Return(true));
//Car car{std::move(mock_engine)};
//or Car car{mock_engine};
//ASSERT_TRUE(car.start());
}
```

**Is the feature specific to an operating system, compiler, or build system version?**

No

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.