google / googletest

GoogleTest - Google Testing and Mocking Framework
https://google.github.io/googletest/
BSD 3-Clause "New" or "Revised" License
34.51k stars 10.09k forks source link

[FR]: ExpectationSet(std::initializer_list<Expectation>) #4559

Closed brandl-muc closed 3 months ago

brandl-muc commented 3 months ago

Does the feature exist in the most recent commit?

No

Why do we need this feature?

In my experience unit testing is pretty repetitive. I usually need to to set up my unit under test with the same set of expectations which may become pretty sophisticated. To make reading the code easier and reduce refactoring overhead in the future I tend to write functions which encapsulate a specific setup of expectations. Often also the need arises to pass in Expectations into such functions to sequence expectations, like such:

Expectation expectCallsBar(MockFoo & mockFoo, Expectation const & expectation)
{
    return EXPECT_CALL(mockFoo, bar()).After(expectation);
}

To make this more general I usually use ExpectationSets to be able to give the empty set as a default argument:

Expectation expectCallsBar(MockFoo & mockFoo, ExpectationSet const & expectations = {})
{
    return EXPECT_CALL(mockFoo, bar()).After(expectations);
}

While this allows for passing no or one expectation there is no easy way to pass more than one expectation. The following call is not possible: auto const barExpectation = expectCallsBar(mockFoo, { previous1, previous2 });

The obvious solution is to add a constructor taking an std::initializer_list<Expectation> if supported by the compiler.

Describe the proposal.

Add the following constructor to ExpectationSet:

  ExpectationSet(std::initializer_list<value_type> const il)
    : expectations_(il)
  {}

This allows for constructing an ExpectationSet inline in function calls like such: expectCallsBar(mockFoo, { previous1, previous2 }); Without this change one needs to write the following: (braces to introduce a scope)

{
  ExpectationSet expectCallsBarSet;
  expectCallsBarSet += previous1;
  expectCallsBarSet += previous 2;
  expectCallsBar(mockFoo, expectCallsbar);
}

This is quite a mouthful and not very desirable.

An obvious workaround is a helper function which compiles the set for you:

  [[nodiscard]] ExpectationSet toExpectationSet(std::initializer_list<Expectation> expectations)
  {
    ExpectationSet result;
    for (auto const & expectation : expectations)
    {
      result += expectation;
    }
    return result;
  }

  template <typename... Expectations,
    class = std::enable_if_t<(... && std::is_same_v<Expectations, Expectation>)>>
  [[nodiscard]] ExpectationSet toExpectationSet(Expectations const & ... expectations)
  {
    return toExpectationSet({ expectations... });
  }

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

<initializer_list> needs a compiler which supports at least C++11, but GTest is C++14 already.

brandl-muc commented 3 months ago

This request is in a similar spirit as #2646, so I'll spare you the trouble of evaluating this FR. The other FR was closed with this comment.