📜 ⬆️ ⬇️

CxxMock - Mock Objects in C ++

If you believe in Agile and developing through testing for you is the norm, and not some kind of incomprehensible practice, but you are probably faced with such a bad problem as organizing the testing of objects that use other objects through C ++ interfaces.

If for .NET there is a great library of Rhino.Mocks , which is enough to “feed” the interface and you get the opportunity to program the behavior of the interface methods right in the unit test. So for C ++ everything is much more complicated, since there is no remarkable reflection that allows you to build code during execution. And you have to write stub objects manually. And if the interface changes, it is necessary not only to update all the classes in the application, but also to update the entire set of “one-time” classes of plugs that implement the interface that are used in the tests.

Decision


I am very lazy, I don’t like to write code that some kind of autogenerka on python or perl can do for me. And I also like the concept: the code should not contain “extra” #define directives as far as possible and, if possible, ensure that all test suites are packaged in classes. That is why among all the unit testing support libraries for C ++, I prefer CxxTest, which uses the concept of a generator of table of contents for all test suites.

The same applies to mock-objects, I do not like to rewrite any one-time classes that implement the stubs of the interfaces used in the tests. And after all, why rewrite them if you can apply the same trick as Rhino.Mocks to .Net but only at the compilation level?
')
No sooner said than done! I represent CxxMock (a mirror on GitHub ).

Remark 1: The library was written a long time ago, it has been applied quite successfully on my projects.

Remark 2: Other solutions exist, for example the same googlemock but it is not ideologically compatible with CxxTest.

Remark 3: CxxMock uses, if possible, the same signature system of expectation methods as Rhino.Mocks , so if you have a project in C # and C ++, then there will be no problem in retraining.

Fast start


1. Add the step of auto-generating the header file containing the implementations of all interfaces that are needed in the tests.

python cxxmockgen.py <IMyCoolInterface.h> <header.h> <header.h>.... >generated_mocks.h 


2. We use in tests, as well as it is done in Rhino.Mocks for C #

 #include "generated_mocks.h" //    . class TestMyMockObjects : public CxxTest::TestSuite { ... ... void testQuickStart() { //   mock- CxxMock::Repository mocks; //     IMyCoolInterface* mock = mocks.create<IMyCoolInterface>(); //   : //   IMyCoolInterface::method()     10   5 TS_EXPECT_CALL( mock->method(10) ).returns( 5 ); //         TS_EXPECT_CALL_VOID( mock->voidMethod() ); //  CxxMock       //          . mocks.replay(); //          //  -    IMyCoolInterface::method()   10 . //      ,   - TS_ASSERT_EQUALS( 5, mock->method(10) ); // -    IMyCoolInterface::voidMethod() //      ,   - mock->voidMethod(); //    //        . mocks.verify(); } } 


two macros with a CxxTest compatible signature are used here.



What else can?


CxxMock does not support all the features of Rhino.Mocks, but the ones that it most needs are supported. Here I will show how to use them.

Setting the return value:

  TS_EXPECT_CALL( object->method() ) .returns( retvalue ); 


Remove the check for the number of calls; by default, only one call is programmed. It is useful if we are not interested in this call, for example, in cases of standard notification of changes.

  TS_EXPECT_CALL( object->method() ) .repeat().any(); 

Explicitly indicate that only one call is waiting. This is the default behavior. If call waiting is set, CxxMock will check that there was only one call.

  TS_EXPECT_CALL( object->method() ) .repeat().once(); 

Explicitly indicate how many method calls to wait. If there are fewer or more calls, verification will fail.

  TS_EXPECT_CALL( object->method() ) .repeat().times(5); 

Do not check arguments. It is useful to apply if we cover the finished system with tests and do not want to understand how it works. it is important for us that we just had calls or just want to skip this check ...

  TS_EXPECT_CALL( object->method() ) .ignoreArguments(); 

An example of a general case of programming is the wait method which will always return the number 10, regardless of the arguments and regardless of the number of calls.

  TS_EXPECT_CALL( object->method("value1", 5) ) .ignoreArguments() .returns( 10 ) .repeat().any(); 

What CxxTest can not?


Anyone even the smartest program always has limitations. Since CxxMock has its own code generator based on your code, the question arises: Will this work with your code?

Answer: Maybe, maybe not. CxxMock supports namespaces, including nested ones, but it is very demanding on method signatures.

For example, such a signature of interface methods is not supported:

 class NotSupportedInterface { public: virtual void canNotSetPointer2(int *arg) = 0; virtual void canNotSetPointer3(int&arg) = 0; virtual void canNotSetReference2(int &arg) = 0; virtual void canNotSetReference3(int&arg) = 0; virtual ~NotSupportedInterface(){} }; 

But these are supported

 class Interface { public: virtual void setValue( Type arg ) = 0; virtual Type getValue() = 0; virtual Type& canGetReference() = 0; virtual Type* canGetPointer() = 0; virtual void canSetPointer( Type* arg ) = 0; virtual void canSetReference(Type& arg) = 0; virtual void canParseCompressed(Type arg1, Type arg2) = 0; virtual Type canParseReference(const Type& arg) = 0; virtual void canSetConstParam(const Type* arg) = 0; virtual void voidMethod() = 0; virtual ~Interface(){} }; 

The basic rule for the arguments is: modifier + type + space + name. In this case, the type must be specified in one word.

And also CxxMock cannot:
  1. CxxMock DOES NOT WORK with abstract classes and templates.
  2. CxxMock Does not have methods for checking arguments by condition; arguments are always checked for equality.
  3. CxxMock Does not support an arbitrary order of waiting for method calls, the order is rigidly defined.
  4. CxxMock Does not support complex types, for example: Type * &, Type <A, B> - use typedef for getting a normal name, it will be easier to debug.
  5. CxxMock Does not support setting actions on a call (the .do () method), it may appear in the future, but now use the manual implementation of the mock object.

Conclusion


If you believe in Agile and use the CxxTest library for unit testing of a C ++ project, but take the time to support manually created stub objects to inspect objects using interfaces, then CxxMock can greatly simplify your task.

Due to the minimal use of #define directives, IDE tools will allow you to always find all the places where one or another interface method is used without the extra cost of maintaining “one-time” objects.

Links


  1. CxxMock main site
  2. SourceForge Mirror
  3. Mirror on github
  4. CxxTest
  5. Rhino.Mocks
  6. GoogleMock

Source: https://habr.com/ru/post/250979/


All Articles