diff options
Diffstat (limited to 'googlemock/docs')
| -rw-r--r-- | googlemock/docs/CookBook.md | 205 | 
1 files changed, 93 insertions, 112 deletions
diff --git a/googlemock/docs/CookBook.md b/googlemock/docs/CookBook.md index c2565f1e..3737d030 100644 --- a/googlemock/docs/CookBook.md +++ b/googlemock/docs/CookBook.md @@ -2229,13 +2229,20 @@ versus  ## Mocking Methods That Use Move-Only Types ## -C++11 introduced <em>move-only types</em>.  A move-only-typed value can be moved from one object to another, but cannot be copied.  `std::unique_ptr<T>` is probably the most commonly used move-only type. +C++11 introduced *move-only types*. A move-only-typed value can be moved from +one object to another, but cannot be copied. `std::unique_ptr<T>` is +probably the most commonly used move-only type. -Mocking a method that takes and/or returns move-only types presents some challenges, but nothing insurmountable.  This recipe shows you how you can do it. +Mocking a method that takes and/or returns move-only types presents some +challenges, but nothing insurmountable. This recipe shows you how you can do it. +Note that the support for move-only method arguments was only introduced to +gMock in April 2017; in older code, you may find more complex +[workarounds](#LegacyMoveOnly) for lack of this feature. -Let’s say we are working on a fictional project that lets one post and share snippets called “buzzes”.  Your code uses these types: +Let’s say we are working on a fictional project that lets one post and share +snippets called “buzzes”. Your code uses these types: -``` +```cpp  enum class AccessLevel { kInternal, kPublic };  class Buzz { @@ -2247,59 +2254,46 @@ class Buzz {  class Buzzer {   public:    virtual ~Buzzer() {} -  virtual std::unique_ptr<Buzz> MakeBuzz(const std::string& text) = 0; -  virtual bool ShareBuzz(std::unique_ptr<Buzz> buzz, Time timestamp) = 0; +  virtual std::unique_ptr<Buzz> MakeBuzz(StringPiece text) = 0; +  virtual bool ShareBuzz(std::unique_ptr<Buzz> buzz, int64_t timestamp) = 0;    ...  };  ``` -A `Buzz` object represents a snippet being posted.  A class that implements the `Buzzer` interface is capable of creating and sharing `Buzz`.  Methods in `Buzzer` may return a `unique_ptr<Buzz>` or take a `unique_ptr<Buzz>`.  Now we need to mock `Buzzer` in our tests. - -To mock a method that returns a move-only type, you just use the familiar `MOCK_METHOD` syntax as usual: - -``` -class MockBuzzer : public Buzzer { - public: -  MOCK_METHOD1(MakeBuzz, std::unique_ptr<Buzz>(const std::string& text)); -  … -}; -``` - -However, if you attempt to use the same `MOCK_METHOD` pattern to mock a method that takes a move-only parameter, you’ll get a compiler error currently: - -``` -  // Does NOT compile! -  MOCK_METHOD2(ShareBuzz, bool(std::unique_ptr<Buzz> buzz, Time timestamp)); -``` - -While it’s highly desirable to make this syntax just work, it’s not trivial and the work hasn’t been done yet.  Fortunately, there is a trick you can apply today to get something that works nearly as well as this. +A `Buzz` object represents a snippet being posted. A class that implements the +`Buzzer` interface is capable of creating and sharing `Buzz`es. Methods in +`Buzzer` may return a `unique_ptr<Buzz>` or take a +`unique_ptr<Buzz>`. Now we need to mock `Buzzer` in our tests. -The trick, is to delegate the `ShareBuzz()` method to a mock method (let’s call it `DoShareBuzz()`) that does not take move-only parameters: +To mock a method that accepts or returns move-only types, you just use the +familiar `MOCK_METHOD` syntax as usual: -``` +```cpp  class MockBuzzer : public Buzzer {   public: -  MOCK_METHOD1(MakeBuzz, std::unique_ptr<Buzz>(const std::string& text)); -  MOCK_METHOD2(DoShareBuzz, bool(Buzz* buzz, Time timestamp)); -  bool ShareBuzz(std::unique_ptr<Buzz> buzz, Time timestamp) { -    return DoShareBuzz(buzz.get(), timestamp); -  } +  MOCK_METHOD1(MakeBuzz, std::unique_ptr<Buzz>(StringPiece text)); +  MOCK_METHOD2(ShareBuzz, bool(std::unique_ptr<Buzz> buzz, int64_t timestamp));  };  ``` -Note that there's no need to define or declare `DoShareBuzz()` in a base class.  You only need to define it as a `MOCK_METHOD` in the mock class. - -Now that we have the mock class defined, we can use it in tests.  In the following code examples, we assume that we have defined a `MockBuzzer` object named `mock_buzzer_`: +Now that we have the mock class defined, we can use it in tests. In the +following code examples, we assume that we have defined a `MockBuzzer` object +named `mock_buzzer_`: -``` +```cpp    MockBuzzer mock_buzzer_;  ``` -First let’s see how we can set expectations on the `MakeBuzz()` method, which returns a `unique_ptr<Buzz>`. +First let’s see how we can set expectations on the `MakeBuzz()` method, which +returns a `unique_ptr<Buzz>`. -As usual, if you set an expectation without an action (i.e. the `.WillOnce()` or `.WillRepeated()` clause), when that expectation fires, the default action for that method will be taken.  Since `unique_ptr<>` has a default constructor that returns a null `unique_ptr`, that’s what you’ll get if you don’t specify an action: +As usual, if you set an expectation without an action (i.e. the `.WillOnce()` or +`.WillRepeated()` clause), when that expectation fires, the default action for +that method will be taken. Since `unique_ptr<>` has a default constructor +that returns a null `unique_ptr`, that’s what you’ll get if you don’t specify an +action: -``` +```cpp    // Use the default action.    EXPECT_CALL(mock_buzzer_, MakeBuzz("hello")); @@ -2307,32 +2301,13 @@ As usual, if you set an expectation without an action (i.e. the `.WillOnce()` or    EXPECT_EQ(nullptr, mock_buzzer_.MakeBuzz("hello"));  ``` -If you are not happy with the default action, you can tweak it.  Depending on what you need, you may either tweak the default action for a specific (mock object, mock method) combination using `ON_CALL()`, or you may tweak the default action for all mock methods that return a specific type.  The usage of `ON_CALL()` is similar to `EXPECT_CALL()`, so we’ll skip it and just explain how to do the latter (tweaking the default action for a specific return type).  You do this via the `DefaultValue<>::SetFactory()` and `DefaultValue<>::Clear()` API: - -``` -  // Sets the default action for return type std::unique_ptr<Buzz> to -  // creating a new Buzz every time. -  DefaultValue<std::unique_ptr<Buzz>>::SetFactory( -      [] { return MakeUnique<Buzz>(AccessLevel::kInternal); }); - -  // When this fires, the default action of MakeBuzz() will run, which -  // will return a new Buzz object. -  EXPECT_CALL(mock_buzzer_, MakeBuzz("hello")).Times(AnyNumber()); +If you are not happy with the default action, you can tweak it as usual; see +[Setting Default Actions](#OnCall). -  auto buzz1 = mock_buzzer_.MakeBuzz("hello"); -  auto buzz2 = mock_buzzer_.MakeBuzz("hello"); -  EXPECT_NE(nullptr, buzz1); -  EXPECT_NE(nullptr, buzz2); -  EXPECT_NE(buzz1, buzz2); +If you just need to return a pre-defined move-only value, you can use the +`Return(ByMove(...))` action: -  // Resets the default action for return type std::unique_ptr<Buzz>, -  // to avoid interfere with other tests. -  DefaultValue<std::unique_ptr<Buzz>>::Clear(); -``` - -What if you want the method to do something other than the default action?  If you just need to return a pre-defined move-only value, you can use the `Return(ByMove(...))` action: - -``` +```cpp    // When this fires, the unique_ptr<> specified by ByMove(...) will    // be returned.    EXPECT_CALL(mock_buzzer_, MakeBuzz("world")) @@ -2343,81 +2318,87 @@ What if you want the method to do something other than the default action?  If y  Note that `ByMove()` is essential here - if you drop it, the code won’t compile. -Quiz time!  What do you think will happen if a `Return(ByMove(...))` action is performed more than once (e.g. you write `….WillRepeatedly(Return(ByMove(...)));`)?  Come think of it, after the first time the action runs, the source value will be consumed (since it’s a move-only value), so the next time around, there’s no value to move from -- you’ll get a run-time error that `Return(ByMove(...))` can only be run once. +Quiz time! What do you think will happen if a `Return(ByMove(...))` action is +performed more than once (e.g. you write +`….WillRepeatedly(Return(ByMove(...)));`)? Come think of it, after the first +time the action runs, the source value will be consumed (since it’s a move-only +value), so the next time around, there’s no value to move from -- you’ll get a +run-time error that `Return(ByMove(...))` can only be run once. -If you need your mock method to do more than just moving a pre-defined value, remember that you can always use `Invoke()` to call a lambda or a callable object, which can do pretty much anything you want: +If you need your mock method to do more than just moving a pre-defined value, +remember that you can always use a lambda or a callable object, which can do +pretty much anything you want: -``` +```cpp    EXPECT_CALL(mock_buzzer_, MakeBuzz("x")) -      .WillRepeatedly(Invoke([](const std::string& text) { -        return std::make_unique<Buzz>(AccessLevel::kInternal); -      })); +      .WillRepeatedly([](StringPiece text) { +        return MakeUnique<Buzz>(AccessLevel::kInternal); +      });    EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("x"));    EXPECT_NE(nullptr, mock_buzzer_.MakeBuzz("x"));  ``` -Every time this `EXPECT_CALL` fires, a new `unique_ptr<Buzz>` will be created and returned.  You cannot do this with `Return(ByMove(...))`. +Every time this `EXPECT_CALL` fires, a new `unique_ptr<Buzz>` will be +created and returned. You cannot do this with `Return(ByMove(...))`. -Now there’s one topic we haven’t covered: how do you set expectations on `ShareBuzz()`, which takes a move-only-typed parameter?  The answer is you don’t.  Instead, you set expectations on the `DoShareBuzz()` mock method (remember that we defined a `MOCK_METHOD` for `DoShareBuzz()`, not `ShareBuzz()`): +That covers returning move-only values; but how do we work with methods +accepting move-only arguments? The answer is that they work normally, although +some actions will not compile when any of method's arguments are move-only. You +can always use `Return`, or a [lambda or functor](#FunctionsAsActions): -``` -  EXPECT_CALL(mock_buzzer_, DoShareBuzz(NotNull(), _)); +```cpp +  using ::testing::Unused; -  // When one calls ShareBuzz() on the MockBuzzer like this, the call is -  // forwarded to DoShareBuzz(), which is mocked.  Therefore this statement -  // will trigger the above EXPECT_CALL. -  mock_buzzer_.ShareBuzz(MakeUnique<Buzz>(AccessLevel::kInternal), -                         ::base::Now()); +  EXPECT_CALL(mock_buzzer_, ShareBuzz(NotNull(), _)) .WillOnce(Return(true)); +  EXPECT_TRUE(mock_buzzer_.ShareBuzz(MakeUnique<Buzz>(AccessLevel::kInternal)), +              0); + +  EXPECT_CALL(mock_buzzer_, ShareBuzz(_, _)) .WillOnce( +      [](std::unique_ptr<Buzz> buzz, Unused) { return buzz != nullptr; }); +  EXPECT_FALSE(mock_buzzer_.ShareBuzz(nullptr, 0));  ``` -Some of you may have spotted one problem with this approach: the `DoShareBuzz()` mock method differs from the real `ShareBuzz()` method in that it cannot take ownership of the buzz parameter - `ShareBuzz()` will always delete buzz after `DoShareBuzz()` returns.  What if you need to save the buzz object somewhere for later use when `ShareBuzz()` is called?  Indeed, you'd be stuck. +Many built-in actions (`WithArgs`, `WithoutArgs`,`DeleteArg`, `SaveArg`, ...) +could in principle support move-only arguments, but the support for this is not +implemented yet. If this is blocking you, please file a bug. -Another problem with the `DoShareBuzz()` we had is that it can surprise people reading or maintaining the test, as one would expect that `DoShareBuzz()` has (logically) the same contract as `ShareBuzz()`. +A few actions (e.g. `DoAll`) copy their arguments internally, so they can never +work with non-copyable objects; you'll have to use functors instead. -Fortunately, these problems can be fixed with a bit more code.  Let's try to get it right this time: +##### Legacy workarounds for move-only types {#LegacyMoveOnly} -``` +Support for move-only function arguments was only introduced to gMock in April +2017. In older code, you may encounter the following workaround for the lack of +this feature (it is no longer necessary - we're including it just for +reference): + +```cpp  class MockBuzzer : public Buzzer {   public: -  MockBuzzer() { -    // Since DoShareBuzz(buzz, time) is supposed to take ownership of -    // buzz, define a default behavior for DoShareBuzz(buzz, time) to -    // delete buzz. -    ON_CALL(*this, DoShareBuzz(_, _)) -        .WillByDefault(Invoke([](Buzz* buzz, Time timestamp) { -          delete buzz; -          return true; -        })); -  } - -  MOCK_METHOD1(MakeBuzz, std::unique_ptr<Buzz>(const std::string& text)); - -  // Takes ownership of buzz.    MOCK_METHOD2(DoShareBuzz, bool(Buzz* buzz, Time timestamp)); -  bool ShareBuzz(std::unique_ptr<Buzz> buzz, Time timestamp) { -    return DoShareBuzz(buzz.release(), timestamp); +  bool ShareBuzz(std::unique_ptr<Buzz> buzz, Time timestamp) override { +    return DoShareBuzz(buzz.get(), timestamp);    }  };  ``` -Now, the mock `DoShareBuzz()` method is free to save the buzz argument for later use if this is what you want: +The trick is to delegate the `ShareBuzz()` method to a mock method (let’s call +it `DoShareBuzz()`) that does not take move-only parameters. Then, instead of +setting expectations on `ShareBuzz()`, you set them on the `DoShareBuzz()` mock +method: -``` -  std::unique_ptr<Buzz> intercepted_buzz; -  EXPECT_CALL(mock_buzzer_, DoShareBuzz(NotNull(), _)) -      .WillOnce(Invoke([&intercepted_buzz](Buzz* buzz, Time timestamp) { -        // Save buzz in intercepted_buzz for analysis later. -        intercepted_buzz.reset(buzz); -        return false; -      })); +```cpp +  MockBuzzer mock_buzzer_; +  EXPECT_CALL(mock_buzzer_, DoShareBuzz(NotNull(), _)); -  mock_buzzer_.ShareBuzz(std::make_unique<Buzz>(AccessLevel::kInternal), -                         Now()); -  EXPECT_NE(nullptr, intercepted_buzz); +  // When one calls ShareBuzz() on the MockBuzzer like this, the call is +  // forwarded to DoShareBuzz(), which is mocked.  Therefore this statement +  // will trigger the above EXPECT_CALL. +  mock_buzzer_.ShareBuzz(MakeUnique<Buzz>(AccessLevel::kInternal), 0);  ``` -Using the tricks covered in this recipe, you are now able to mock methods that take and/or return move-only types.  Put your newly-acquired power to good use - when you design a new API, you can now feel comfortable using `unique_ptrs` as appropriate, without fearing that doing so will compromise your tests. +  ## Making the Compilation Faster ##  | 
