Nicolás Brailovsky


A modern blog

C++: Checking if a method exists in a parent class

author Posted by: nico on date Dec 8th, 2011 | filed Filed under: C++

I like Google test and Google mock (C++ only) a lot. These are really great tools to ensure the quality of your code. They do have one problem however, especially when working with a legacy codebase: many times you need to change a signature for a function and your tests begin to fail. Those tests shouldn’t really fail, they shouldn’t compile at all because you’re now trying to mock a function which doesn’t exists anymore.

I worked on a patch to check if a class and its parent share the same methods, but I hit some major roadblocks which I believe cannot be saved:

  • You have no way of detecting if the parent’s method is actually virtual. It may have the same signature, yet if it isn’t virtual the mock serves no purpose
  • You have no (easy) way of detecting if the method is defined in your parent’s parent

Even though this code will never be useful, I thought I might as well post this here, just in case anyone comes up with a solution for those problems.

  1. // :!g++ foo.cpp -lgtest_main -lgmock && ./a.out
  2. #include <gtest/gtest.h>
  3. #include <gmock/gmock.h>
  4. using namespace testing;
  5.  
  6. /**
  7.  * "Real" application
  8.  */
  9. class Foo {
  10.         public:
  11.         virtual int bar(int) { return 1; }
  12.         //virtual int bar(void) { return 1; }
  13. };
  14.  
  15. class Do {
  16.         public:
  17.         int something(Foo &amp;foo){ return foo.bar(1); }
  18. };
  19.  
  20. /**
  21.  * Class used to compare method ptrs. We need to inherit from class C
  22.  * to forward the calls to the derived methods.
  23.  */
  24. template <class C, class D>
  25. class Mocks_Must_Exist_In : public C {
  26.         public:
  27.         // We don’t know in the derived class the typeof the parent class
  28.         // so we define a common name using some template magic
  29.         typedef C ParentClass;
  30.         // Actually we don’t know our own class either
  31.         typedef D Self;
  32.  
  33.         // Function ptr definitions are ugly so we might as well use
  34.         // a template to hide it under the rug
  35.         template <class F, class G>
  36.         void mock_created_for_unexisting_method(F f, G g){ f = g; }
  37. };
  38.  
  39. #define METHOD_EXISTS(Method) \
  40.         void defined_##Method() { \
  41.                 mock_created_for_unexisting_method(&amp;Self::Method, &amp;ParentClass::Method); \
  42.         } \
  43.  
  44. /**
  45.  * Checked mock, shouldn’t compile if Foo’s interface changes
  46.  */
  47. class FooMock : public Mocks_Must_Exist_In< Foo, FooMock > {
  48.         public:
  49.         MOCK_METHOD1(bar, int(int));
  50.         METHOD_EXISTS(bar);
  51. };
  52.  
  53. /**
  54.  * Unchecked mock, should compile if Foo’s interface changes
  55.  */
  56. class FooMock2 {
  57.         public:
  58.         MOCK_METHOD0(bar, int());
  59. };
  60.  
  61. TEST(FooTest, ThisShouldCompile) {
  62.         FooMock foo;
  63.         EXPECT_CALL(foo, bar(_)).WillOnce(Return(42));
  64.         EXPECT_EQ(42, Do().something(foo));
  65. }
  66.  


     Add A Comment

trackback Trackback URI | rsscomment Comments RSS