Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
3 tasks
paulreimer opened this issue Dec 18, 2017 · 3 comments
Open
3 tasks

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

paulreimer opened this issue Dec 18, 2017 · 3 comments

Comments

@paulreimer
Copy link

paulreimer commented Dec 18, 2017

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?

@krzysztof-jusiak
Copy link
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>()

@paulreimer
Copy link
Author

paulreimer commented Dec 18, 2017

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)

@krzysztof-jusiak
Copy link
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
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants