Thursday, November 15, 2012

Marriage of CxxUnit and GMock

Ok, this is not quite Java, but sometimes you might need stuff like that. So let's do it.

Based on "Using Google Mock with Any Testing Framework", CxxUnit Template Files and "Hello World" mocking examples we get:
1) provide CxxUnit with custom template for the main function:
CXXTESTGEN_FLAGS +=                \
 --runner=ParenPrinter                 \
 --template=C:\\src\\...\\TEMPLATE.tpl \
 --have-eh                             \
 --abort-on-fail
where TEMPLATE.ptl is something like:
<CxxTest preamble>

#include "gtest/gtest.h"
#include "gmock/gmock.h"

int main(int argc, char *argv[]) {
   // The following line causes Google Mock to throw an exception on failure,
   // which will be interpreted by your testing framework as a test failure.
   ::testing::GTEST_FLAG(throw_on_failure) = true;
   ::testing::InitGoogleMock(&argc, argv);
   // whatever your testing framework requires...
   return CxxTest::GuiTuiRunner<CxxTest::Win32Gui, CxxTest::ParenPrinter>(argc, argv).run();
}

// The CxxTest "world"
<CxxTest world>
this will result into something like:
/* Generated file, do not edit */

#ifndef CXXTEST_RUNNING
#define CXXTEST_RUNNING
#endif

#define _CXXTEST_HAVE_STD
#define _CXXTEST_HAVE_EH
#define _CXXTEST_ABORT_TEST_ON_FAIL
#include <cxxtest/TestListener.h>
#include <cxxtest/TestTracker.h>
#include <cxxtest/TestRunner.h>
#include <cxxtest/RealDescriptions.h>
#include <cxxtest/ParenPrinter.h>
#include <cxxtest/Win32Gui.h>


#include "gtest/gtest.h"
#include "gmock/gmock.h"

int main(int argc, char *argv[]) {
   // The following line causes Google Mock to throw 
   // an exception on failure, which will be interpreted 
   // by your testing framework as a test failure.
   ::testing::GTEST_FLAG(throw_on_failure) = true;
   ::testing::InitGoogleMock(&argc, argv);
   // whatever your testing framework requires...
   return CxxTest::GuiTuiRunner<CxxTest::Win32Gui, CxxTest::ParenPrinter>
      (argc, argv).run();
}

// The CxxTest "world"
#include "C:/src/.../HelloGMockTest.h"

2) reference the library:
LD_OPTS += -libpath:C:\\...\\win32\\gmock-1.4.0-vc90sp1\\lib-debug LIBS = ... gmock.lib
3) run tests:
class Foo{
     public:
      virtual ~Foo() {}
      virtual void doSomethingUseful() = 0;
    };

   class Bar
    {
      public:
      void addFoo(Foo& f)
      {
        // comment to fail
        f.doSomethingUseful();
      }
    };

   class MockFoo : public Foo
      {
      public:

         MOCK_METHOD0(doSomethingUseful, void());
      };

   void testGTestPass() {
      EXPECT_EQ(42, 42);
   }

   void testGTestFail() {
      EXPECT_EQ(42, 41);
   }

   void testGMock() {
      // Create Mock and set up expectations.
        MockFoo foo;
        EXPECT_CALL(foo, doSomethingUseful()).Times(1);
        // Use the mock type in test type.
        Bar bar;
        bar.addFoo(foo);
        // Verify that Bar has done the expected thing with Foo. Will fail and give descriptive message if not.
        TSM_ASSERT("Bar has not used Foo as expected.", ::testing::Mock::VerifyAndClearExpectations(&foo));
   }

A couple of gotchas:
- don't link against gmock_main.lib as it will mask your "main" function
- link against debug version (if you use /MT or /MD flags) of the lib or you'll get "warning LNK4098: defaultlib 'msvcrt.lib' conflicts with use of other libs; use /NODEFAULTLIB:library" and your test will crash

That's it for now :)