Googletest export

Give each of Naggy/Nice/StrictMock a base class whose constructor runs before
the mocked class's constructor, and a destructor that runs after the mocked
class's destructor, so that any mock methods run in either the constructor or
destructor use the same strictness as other calls.

PiperOrigin-RevId: 348511612
This commit is contained in:
Abseil Team 2020-12-21 15:41:17 -05:00 committed by Derek Mauro
parent 4f6fa70870
commit ca4b7c9ff4
4 changed files with 121 additions and 54 deletions

View File

@ -429,16 +429,6 @@ limitations):
2. `NiceMock<MockFoo>` and `StrictMock<MockFoo>` may not work correctly if the 2. `NiceMock<MockFoo>` and `StrictMock<MockFoo>` may not work correctly if the
destructor of `MockFoo` is not virtual. We would like to fix this, but it destructor of `MockFoo` is not virtual. We would like to fix this, but it
requires cleaning up existing tests. requires cleaning up existing tests.
3. During the constructor or destructor of `MockFoo`, the mock object is *not*
nice or strict. This may cause surprises if the constructor or destructor
calls a mock method on `this` object. (This behavior, however, is consistent
with C++'s general rule: if a constructor or destructor calls a virtual
method of `this` object, that method is treated as non-virtual. In other
words, to the base class's constructor or destructor, `this` object behaves
like an instance of the base class, not the derived class. This rule is
required for safety. Otherwise a base constructor may use members of a
derived class before they are initialized, or a base destructor may use
members of a derived class after they have been destroyed.)
Finally, you should be **very cautious** about when to use naggy or strict Finally, you should be **very cautious** about when to use naggy or strict
mocks, as they tend to make tests more brittle and harder to maintain. When you mocks, as they tend to make tests more brittle and harder to maintain. When you

View File

@ -90,10 +90,51 @@ constexpr bool HasStrictnessModifier() {
return decltype(StrictnessModifierProbe(std::declval<const T&>()))::value; return decltype(StrictnessModifierProbe(std::declval<const T&>()))::value;
} }
// Base classes that register and deregister with testing::Mock to alter the
// default behavior around uninteresting calls. Inheriting from one of these
// classes first and then MockClass ensures the MockClass constructor is run
// after registration, and that the MockClass destructor runs before
// deregistration. This guarantees that MockClass's constructor and destructor
// run with the same level of strictness as its instance methods.
#if GTEST_OS_WINDOWS && (defined(_MSC_VER) || defined(__clang__))
// We need to mark these classes with this declspec to ensure that
// the empty base class optimization is performed.
#define GTEST_INTERNAL_EMPTY_BASE_CLASS __declspec(empty_bases)
#else
#define GTEST_INTERNAL_EMPTY_BASE_CLASS
#endif
template <typename Base>
class NiceMockImpl {
public:
NiceMockImpl() { ::testing::Mock::AllowUninterestingCalls(this); }
~NiceMockImpl() { ::testing::Mock::UnregisterCallReaction(this); }
};
template <typename Base>
class NaggyMockImpl {
public:
NaggyMockImpl() { ::testing::Mock::WarnUninterestingCalls(this); }
~NaggyMockImpl() { ::testing::Mock::UnregisterCallReaction(this); }
};
template <typename Base>
class StrictMockImpl {
public:
StrictMockImpl() { ::testing::Mock::FailUninterestingCalls(this); }
~StrictMockImpl() { ::testing::Mock::UnregisterCallReaction(this); }
};
} // namespace internal } // namespace internal
template <class MockClass> template <class MockClass>
class NiceMock : public MockClass { class GTEST_INTERNAL_EMPTY_BASE_CLASS NiceMock
: private internal::NiceMockImpl<MockClass>,
public MockClass {
public: public:
static_assert( static_assert(
!internal::HasStrictnessModifier<MockClass>(), !internal::HasStrictnessModifier<MockClass>(),
@ -102,8 +143,8 @@ class NiceMock : public MockClass {
"https://github.com/google/googletest/blob/master/googlemock/docs/" "https://github.com/google/googletest/blob/master/googlemock/docs/"
"cook_book.md#the-nice-the-strict-and-the-naggy-nicestrictnaggy"); "cook_book.md#the-nice-the-strict-and-the-naggy-nicestrictnaggy");
NiceMock() : MockClass() { NiceMock() : MockClass() {
::testing::Mock::AllowUninterestingCalls( static_assert(sizeof(*this) == sizeof(MockClass),
internal::ImplicitCast_<MockClass*>(this)); "The impl subclass shouldn't introduce any padding");
} }
// Ideally, we would inherit base class's constructors through a using // Ideally, we would inherit base class's constructors through a using
@ -115,21 +156,16 @@ class NiceMock : public MockClass {
// made explicit. // made explicit.
template <typename A> template <typename A>
explicit NiceMock(A&& arg) : MockClass(std::forward<A>(arg)) { explicit NiceMock(A&& arg) : MockClass(std::forward<A>(arg)) {
::testing::Mock::AllowUninterestingCalls( static_assert(sizeof(*this) == sizeof(MockClass),
internal::ImplicitCast_<MockClass*>(this)); "The impl subclass shouldn't introduce any padding");
} }
template <typename TArg1, typename TArg2, typename... An> template <typename TArg1, typename TArg2, typename... An>
NiceMock(TArg1&& arg1, TArg2&& arg2, An&&... args) NiceMock(TArg1&& arg1, TArg2&& arg2, An&&... args)
: MockClass(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2), : MockClass(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2),
std::forward<An>(args)...) { std::forward<An>(args)...) {
::testing::Mock::AllowUninterestingCalls( static_assert(sizeof(*this) == sizeof(MockClass),
internal::ImplicitCast_<MockClass*>(this)); "The impl subclass shouldn't introduce any padding");
}
~NiceMock() { // NOLINT
::testing::Mock::UnregisterCallReaction(
internal::ImplicitCast_<MockClass*>(this));
} }
private: private:
@ -137,7 +173,9 @@ class NiceMock : public MockClass {
}; };
template <class MockClass> template <class MockClass>
class NaggyMock : public MockClass { class GTEST_INTERNAL_EMPTY_BASE_CLASS NaggyMock
: private internal::NaggyMockImpl<MockClass>,
public MockClass {
static_assert( static_assert(
!internal::HasStrictnessModifier<MockClass>(), !internal::HasStrictnessModifier<MockClass>(),
"Can't apply NaggyMock to a class hierarchy that already has a " "Can't apply NaggyMock to a class hierarchy that already has a "
@ -147,8 +185,8 @@ class NaggyMock : public MockClass {
public: public:
NaggyMock() : MockClass() { NaggyMock() : MockClass() {
::testing::Mock::WarnUninterestingCalls( static_assert(sizeof(*this) == sizeof(MockClass),
internal::ImplicitCast_<MockClass*>(this)); "The impl subclass shouldn't introduce any padding");
} }
// Ideally, we would inherit base class's constructors through a using // Ideally, we would inherit base class's constructors through a using
@ -160,21 +198,16 @@ class NaggyMock : public MockClass {
// made explicit. // made explicit.
template <typename A> template <typename A>
explicit NaggyMock(A&& arg) : MockClass(std::forward<A>(arg)) { explicit NaggyMock(A&& arg) : MockClass(std::forward<A>(arg)) {
::testing::Mock::WarnUninterestingCalls( static_assert(sizeof(*this) == sizeof(MockClass),
internal::ImplicitCast_<MockClass*>(this)); "The impl subclass shouldn't introduce any padding");
} }
template <typename TArg1, typename TArg2, typename... An> template <typename TArg1, typename TArg2, typename... An>
NaggyMock(TArg1&& arg1, TArg2&& arg2, An&&... args) NaggyMock(TArg1&& arg1, TArg2&& arg2, An&&... args)
: MockClass(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2), : MockClass(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2),
std::forward<An>(args)...) { std::forward<An>(args)...) {
::testing::Mock::WarnUninterestingCalls( static_assert(sizeof(*this) == sizeof(MockClass),
internal::ImplicitCast_<MockClass*>(this)); "The impl subclass shouldn't introduce any padding");
}
~NaggyMock() { // NOLINT
::testing::Mock::UnregisterCallReaction(
internal::ImplicitCast_<MockClass*>(this));
} }
private: private:
@ -182,7 +215,9 @@ class NaggyMock : public MockClass {
}; };
template <class MockClass> template <class MockClass>
class StrictMock : public MockClass { class GTEST_INTERNAL_EMPTY_BASE_CLASS StrictMock
: private internal::StrictMockImpl<MockClass>,
public MockClass {
public: public:
static_assert( static_assert(
!internal::HasStrictnessModifier<MockClass>(), !internal::HasStrictnessModifier<MockClass>(),
@ -191,8 +226,8 @@ class StrictMock : public MockClass {
"https://github.com/google/googletest/blob/master/googlemock/docs/" "https://github.com/google/googletest/blob/master/googlemock/docs/"
"cook_book.md#the-nice-the-strict-and-the-naggy-nicestrictnaggy"); "cook_book.md#the-nice-the-strict-and-the-naggy-nicestrictnaggy");
StrictMock() : MockClass() { StrictMock() : MockClass() {
::testing::Mock::FailUninterestingCalls( static_assert(sizeof(*this) == sizeof(MockClass),
internal::ImplicitCast_<MockClass*>(this)); "The impl subclass shouldn't introduce any padding");
} }
// Ideally, we would inherit base class's constructors through a using // Ideally, we would inherit base class's constructors through a using
@ -204,27 +239,24 @@ class StrictMock : public MockClass {
// made explicit. // made explicit.
template <typename A> template <typename A>
explicit StrictMock(A&& arg) : MockClass(std::forward<A>(arg)) { explicit StrictMock(A&& arg) : MockClass(std::forward<A>(arg)) {
::testing::Mock::FailUninterestingCalls( static_assert(sizeof(*this) == sizeof(MockClass),
internal::ImplicitCast_<MockClass*>(this)); "The impl subclass shouldn't introduce any padding");
} }
template <typename TArg1, typename TArg2, typename... An> template <typename TArg1, typename TArg2, typename... An>
StrictMock(TArg1&& arg1, TArg2&& arg2, An&&... args) StrictMock(TArg1&& arg1, TArg2&& arg2, An&&... args)
: MockClass(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2), : MockClass(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2),
std::forward<An>(args)...) { std::forward<An>(args)...) {
::testing::Mock::FailUninterestingCalls( static_assert(sizeof(*this) == sizeof(MockClass),
internal::ImplicitCast_<MockClass*>(this)); "The impl subclass shouldn't introduce any padding");
}
~StrictMock() { // NOLINT
::testing::Mock::UnregisterCallReaction(
internal::ImplicitCast_<MockClass*>(this));
} }
private: private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(StrictMock); GTEST_DISALLOW_COPY_AND_ASSIGN_(StrictMock);
}; };
#undef GTEST_INTERNAL_EMPTY_BASE_CLASS
} // namespace testing } // namespace testing
#endif // GMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_ #endif // GMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_

View File

@ -108,6 +108,14 @@ template <typename F> class TypedExpectation;
// Helper class for testing the Expectation class template. // Helper class for testing the Expectation class template.
class ExpectationTester; class ExpectationTester;
// Helper classes for implementing NiceMock, StrictMock, and NaggyMock.
template <typename MockClass>
class NiceMockImpl;
template <typename MockClass>
class StrictMockImpl;
template <typename MockClass>
class NaggyMockImpl;
// Protects the mock object registry (in class Mock), all function // Protects the mock object registry (in class Mock), all function
// mockers, and all expectations. // mockers, and all expectations.
// //
@ -413,14 +421,12 @@ class GTEST_API_ Mock {
template <typename F> template <typename F>
friend class internal::FunctionMocker; friend class internal::FunctionMocker;
template <typename M> template <typename MockClass>
friend class NiceMock; friend class internal::NiceMockImpl;
template <typename MockClass>
template <typename M> friend class internal::NaggyMockImpl;
friend class NaggyMock; template <typename MockClass>
friend class internal::StrictMockImpl;
template <typename M>
friend class StrictMock;
// Tells Google Mock to allow uninteresting calls on the given mock // Tells Google Mock to allow uninteresting calls on the given mock
// object. // object.

View File

@ -67,6 +67,12 @@ class NotDefaultConstructible {
explicit NotDefaultConstructible(int) {} explicit NotDefaultConstructible(int) {}
}; };
class CallsMockMethodInDestructor {
public:
~CallsMockMethodInDestructor() { OnDestroy(); }
MOCK_METHOD(void, OnDestroy, ());
};
// Defines some mock classes needed by the tests. // Defines some mock classes needed by the tests.
class Foo { class Foo {
@ -302,6 +308,13 @@ TEST(NiceMockTest, AcceptsClassNamedMock) {
nice.DoThis(); nice.DoThis();
} }
TEST(NiceMockTest, IsNiceInDestructor) {
{
NiceMock<CallsMockMethodInDestructor> nice_on_destroy;
// Don't add an expectation for the call before the mock goes out of scope.
}
}
TEST(NiceMockTest, IsNaggy_IsNice_IsStrict) { TEST(NiceMockTest, IsNaggy_IsNice_IsStrict) {
NiceMock<MockFoo> nice_foo; NiceMock<MockFoo> nice_foo;
EXPECT_FALSE(Mock::IsNaggy(&nice_foo)); EXPECT_FALSE(Mock::IsNaggy(&nice_foo));
@ -405,6 +418,22 @@ TEST(NaggyMockTest, AcceptsClassNamedMock) {
naggy.DoThis(); naggy.DoThis();
} }
TEST(NaggyMockTest, IsNaggyInDestructor) {
const std::string saved_flag = GMOCK_FLAG(verbose);
GMOCK_FLAG(verbose) = "warning";
CaptureStdout();
{
NaggyMock<CallsMockMethodInDestructor> naggy_on_destroy;
// Don't add an expectation for the call before the mock goes out of scope.
}
EXPECT_THAT(GetCapturedStdout(),
HasSubstr("Uninteresting mock function call"));
GMOCK_FLAG(verbose) = saved_flag;
}
TEST(NaggyMockTest, IsNaggy_IsNice_IsStrict) { TEST(NaggyMockTest, IsNaggy_IsNice_IsStrict) {
NaggyMock<MockFoo> naggy_foo; NaggyMock<MockFoo> naggy_foo;
EXPECT_TRUE(Mock::IsNaggy(&naggy_foo)); EXPECT_TRUE(Mock::IsNaggy(&naggy_foo));
@ -489,6 +518,16 @@ TEST(StrictMockTest, AcceptsClassNamedMock) {
strict.DoThis(); strict.DoThis();
} }
TEST(StrictMockTest, IsStrictInDestructor) {
EXPECT_NONFATAL_FAILURE(
{
StrictMock<CallsMockMethodInDestructor> strict_on_destroy;
// Don't add an expectation for the call before the mock goes out of
// scope.
},
"Uninteresting mock function call");
}
TEST(StrictMockTest, IsNaggy_IsNice_IsStrict) { TEST(StrictMockTest, IsNaggy_IsNice_IsStrict) {
StrictMock<MockFoo> strict_foo; StrictMock<MockFoo> strict_foo;
EXPECT_FALSE(Mock::IsNaggy(&strict_foo)); EXPECT_FALSE(Mock::IsNaggy(&strict_foo));