Mocking the whole library

I am developing code that uses boost::asio . To test this, I need to make fun of the set of classes from this library. I use Google Mock, which allows me to mock virtual methods. The usual (and tedious) process would be to write an interface for each of the classes that I need to use.

On the other hand, the Google Mock Cookbook describes an alternative when it comes to mocking non-virtual methods: using templates. The problem in my case is that I may need to add several classes to several add-ons simultaneously (so using templates will not work directly). So I thought: why not use two-level templates? I came up with the following solution:

 // Classes to be mocked. class RealA { public: void a() { cout << "RealA::a()" << endl; }; }; class RealB { public: void b() { cout << "RealB::b()" << endl; }; }; // Mock classes. class MockA { public: void a() { cout << "MockA::a()" << endl; }; }; class MockB { public: void b() { cout << "MockB::b()" << endl; }; }; template<class ABFactory> class Program { public: void setFactory(ABFactory* factory) { factory = factory; } void useA() { typename ABFactory::A* a = factory->createA(); a->a(); delete a; } void useB() { typename ABFactory::B b; bb(); } private: ABFactory* factory; }; template<class ParamA, class ParamB> class TABFactory { public: typedef ParamA A; typedef ParamB B; A* createA() { return new A; }; B* createB() { return new B; }; }; typedef TABFactory<RealA, RealB> RealABFactory; typedef TABFactory<MockA, MockB> MockABFactory; 

Then normal use:

 Program<RealABFactory> p; p.useA(); p.useB(); 

So far, the test will look like this:

 Program<MockABFactory> t; t.useA(); t.useB(); 

This starts to get complicated when the mocking classes have methods with complex parameters (for example, other classes from the same library that cannot be mocked). So it does not seem to scale. Any thoughts on this solution or suggestions for other approaches to the problem?

+5
c ++ mocking boost-asio googletest googlemock
Apr 21 '12 at 15:43
source share
1 answer

this is an ideal use case for python cog

see also this answer .

I used cog to create handlers for the event list, the handler code is very general, and I do not need to do special cases, but I still have to write all the functions, so I made the events in the list in the .py file and the code to generate the handler template in the function python. so I can be true to the DRY principle

obviously you will need to add cog to the pre-build of your makefile to work with your toolchain

To change as an example the development of the code needed to add a template to your classes, I would do something like:

myCodeGeneration.py

 import cog ClassesToMock = [ [ 'IfaceA' , 'classA' , 'mockA' , ['void doSomething(int foo)' , 'int getSomething()'] , [ 'IfaceB', 'classB' , 'mockB' , ['static classA& getInstance()'] ] def addInterfaces( myStructure ): for classItem in myStructure: cog.outl('class %s { ' % classItem[0] ) for methodDecl in classItem[3]: cog.outl(' virtual %s = 0;' %methodDecl ) cog.outl(' } ') #implement your real classes normally def addMocks( myStructure ): for classItem in myStructure: cog.outl('class %s : public %s { ' % classItem[2] % classItem[0] ) for methodDecl in classItem[3]: cog.outl(' %s {' %methodDecl ) cog.outl(' MOCK_STUFF_MACRO ') cog.outl(' } ') cog.outl(' } ') 

then in your header:

IfaceA.h

 /*[[[cog import cog import myCodeGeneration myCodeGeneration.addInterfaces( [ [ 'IfaceA' , 'classA' , 'mockA' , ['void doSomething(int foo)' , 'int getSomething()'] ] ) ]]]*/ //your code will be generated here //[[[end]]] 

mockA.h

 /*[[[cog import cog import myCodeGeneration myCodeGeneration.addMocks( [ [ 'IfaceA' , 'classA' , 'mockA' , ['void doSomething(int foo)' , 'int getSomething()'] ] ) ]]]*/ //your code will be generated here //[[[end]]] 

Also, the problem of considering adding python to your C ++ source โ€œpollutesโ€ it or โ€œdecoratesโ€ it is largely a matter of taste and style. I believe cog provides an addition to metaprogramming a template style that C ++ does not have, providing tools for the programmer to ensure the code is clear and legible. But I do not expect everyone to agree.

For me, the whole principle of architectural design that underlies this approach, Do not repeat yourself . errors occur when we must manually encode a method in several places. Let the computer automate what it automates and encodes things that can happen more than once. As a side effect, it will make your coding more enjoyable, both for writing it and reading it later.

hope this helps

+4
Apr 21 '12 at 15:52
source share



All Articles