Skip to content

"Hi-Perf" (non-virtual) Dependency Injection #16

Open

Description

@paulreimer

If we are using the "Hi-Perf" Google.Mock feature
https://github.com/google/googletest/blob/master/googlemock/docs/CookBook.md#mocking-nonvirtual-methods

Then how does this affect the ideal use of GUnit?

  • Can we still use the automatic mock generation?
  • Can we still use GUnit.GMake? (since we are using derived class template args, not static_cast<iface&>)
  • Can we still use Boost.DI? (I know, a question for a different repo)

Or any other trade-offs from using non-virtual methods?

Activity

  1. kris-jusiak commented on Dec 18, 2017

    @kris-jusiak
    Contributor

    Thanks @paulreimer for the questions

    • Can we still use the automatic mock generation?

      • Unfortunatelly not out of the box as automatic mock injection requres GMocks
      • You could still use it if you provide some sort of traits for the mocks, though
        template<class> struct Mock;
        
        template<>
        struct Mock<MockPacketStream> {
         MOCK_CONST_METHOD1(GetPacket, const Packet*(size_t packet_number));
         MOCK_CONST_METHOD0(NumberOfPackets, size_t());
        };

      And this automatic mocks injection when resolve a type T

      return Mock<T>();
    • Can we still use GUnit.GMake? (since we are using derived class template args, not static_cast<iface&>)

    • Sort of, as described above
    • Can we still use Boost.DI? (I know, a question for a different repo)
    • Yes, no issues here

       example::example(ConcretePacketStream &); // no problems here, wiring not even needed

      With templates no issues too

       template<class T = class Stream>
       example::example(T stream);
      di::bind<class Stream>.to<ConcretePacketStream>()

    One thing, which might be useful when using templates is to avoid writing macros, though.
    That can be done by defining an interface instead and using GMock, automatic mock injection and DI without loosing performance at all as it will be injected via templates.

    struct IStreamJustForTesting {
      virtual ~IStreamJustForTesting () = default;
      virtual void AppendPacket(Packet* new_packet) = 0;
      virtual const Packet* GetPacket(size_t packet_number) const =0;
      virtual size_t NumberOfPackets() const = 0;
    };
    di::bind<class Stream>.to<GMock<IStreamJustForTesting>()
  2. paulreimer commented on Dec 18, 2017

    @paulreimer
    Author

    OK, sounds good. I'm still trying to understand the first "automatic mock injection" part. Actually my question was more about the "automatic mock generation" part (e.g. avoiding macros), looks like it is possible as in the final example. So, at least we can define the interface in C++ (even if the concrete class doesn't inherit it) and avoid the MOCK_* macros (yay!).

    Also just to check if I understand correctly, but in the final example then any mocks generated for IStreamJustForTesting will have dynamic dispatch (and use static_cast), but this only applies to the mocks and the production implementations do not need that interface? (and this is the price we pay -- at test time -- for avoiding the macros)

  3. kris-jusiak commented on Dec 18, 2017

    @kris-jusiak
    Contributor

    Right, Macros might be avoided with interfaces and GMock.

    Yes, exactly. No cost in production but dynamic dispatch in testing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions