Thread: RE: [Cppunit-devel] Mock C++
Brought to you by:
blep
From: Moran Ben-D. <mbe...@ii...> - 2002-08-13 15:26:27
|
I'm unfamiliar with Mock objects.. do you mind giving a better explanation than the one in easymock.org? For some reason, the author chose to use the word Mock to describe Mock objects. A bit confusing for the layman. thanks, moran -----Original Message----- From: Philippe FREMY [mailto:P....@OB...] Sent: Tuesday, August 13, 2002 5:15 AM To: cpp...@li... Subject: [Cppunit-devel] Mock C++ Hi, Have you guys thought about doing Mock objects for C++ ? It comes naturally after Junit. More info about mocks: http://www.easymock.org/readme.html http://www.mockobjects.com We probably can not reach the versatility of Java here, but we can sure do something. regards, Philippe ------------------------------------------------------- This sf.net email is sponsored by: Dice - The leading online job board for high-tech professionals. Search and apply for tech jobs today! http://seeker.dice.com/seeker.epl?rel_code=31 _______________________________________________ Cppunit-devel mailing list Cpp...@li... https://lists.sourceforge.net/lists/listinfo/cppunit-devel |
From: King D. <Ki...@tc...> - 2002-08-13 19:49:33
|
> Have you guys thought about doing Mock objects for C++ ? It comes naturally > after Junit. > We probably can not reach the versatility of Java here, but we can sure do > something. I've thought about it a lot and done some work in that direction and also got input from someone else that started on it (and didn't get any farther than me). Unfortunately, there are so many things about Javaa that make it so much easier than with C++. Chief among those is reflection. With mock objects in Java just by having a member of the instance be a subclass of Verifiable it will get verified. There is no easy way to do that in C++. You end up having to do things like with CppUnit where you use complex macros to add things to be verified. I also looked at using something like OpenC++ which is sort of a preprocessor for C++ which can give you compile time reflection. I'd be happy to share what I started and what other examples I was given, but you could probably get that far yourself pretty easily. -- Dale King |
From: lacall <la...@mi...> - 2002-08-13 20:32:22
|
In case it helps, there seem to be 2-3 articles on this here: http://www.junit.org/news/article/index.htm <http://www.junit.org/news/article/index.htm> ...if you search the page for "mock." Luke -----Original Message----- From: Moran Ben-David [mailto:mbe...@ii...] Sent: Tuesday, August 13, 2002 9:26 AM To: 'Philippe FREMY'; cpp...@li... Subject: RE: [Cppunit-devel] Mock C++ I'm unfamiliar with Mock objects.. do you mind giving a better explanation than the one in easymock.org? For some reason, the author chose to use the word Mock to describe Mock objects. A bit confusing for the layman. thanks, moran -----Original Message----- From: Philippe FREMY [ mailto:P....@OB... <mailto:P....@OB...> ] Sent: Tuesday, August 13, 2002 5:15 AM To: cpp...@li... Subject: [Cppunit-devel] Mock C++ Hi, Have you guys thought about doing Mock objects for C++ ? It comes naturally after Junit. More info about mocks: http://www.easymock.org/readme.html <http://www.easymock.org/readme.html> http://www.mockobjects.com <http://www.mockobjects.com> We probably can not reach the versatility of Java here, but we can sure do something. regards, Philippe ------------------------------------------------------- This sf.net email is sponsored by: Dice - The leading online job board for high-tech professionals. Search and apply for tech jobs today! http://seeker.dice.com/seeker.epl?rel_code=31 <http://seeker.dice.com/seeker.epl?rel_code=31> _______________________________________________ Cppunit-devel mailing list Cpp...@li... https://lists.sourceforge.net/lists/listinfo/cppunit-devel <https://lists.sourceforge.net/lists/listinfo/cppunit-devel> |
From: Philippe F. <P....@OB...> - 2002-08-14 08:21:29
|
> I'm unfamiliar with Mock objects.. do you mind giving a better explanation than the one in easymock.org? > For some reason, the author chose to use the word Mock to describe Mock objects. A bit confusing for the > layman. A mock object is an object that mocks (simulates) a legacy object. You can manipulate it like the object it mocks. It has the additional properties that when its methods are invoked it does nothing except registering the call and returning a defined value. To use it in a test, you pass the mock object to a function you want to test. The function is supposed to perform certain calls on the mock object. When the function returns, you check in your mock objects that the calls are the one you were expecting. Imagine you have a methods that takes a visitor and you want to check if it passes the correct info to the visitor. You would have something like: class Tree : { // [...] void visitTree( Visitor * it ); }; class Visitor { // [...] virtual void visitFoo( Foo * foo); virtual void visitBar( Bar * bar); } In your test, you want to check that visitor is called correctely. The way to do that is to have a mock for the visitor: class MockVisitor : Visitor { // [...] void visitFoo( Foo * foo ) { // register that visitFoo was called with argument foo } void visitBar( Bar * bar ) { // register that visitBar was called with argument bar } }; TestTree::testVisitTree() { Tree tree; tree.load( "Foo1Foo2Bar1" ); // prepare a tree with two Foo1, Foo2 and Bar1 mock = new MockVisitor(); tree.visitTree( mock ); // check that first call to mock was visitFoo( foo1 ) // check that second call to mock was visitFoo( foo2 ) // check that third call to mock was visitBar( bar1 ) } It is far easier to implement that in Java or Python for many reasons. In C++, using a mock requires that all function to be called are virtual, so that they can be overridden in the mock. In this example, the mock does not return anything, but if the function is expected to return a value, the value must be setup before passing the mock. So the requirements for a mock object in C++ are: - an object that can be called like another object it mimicks. I only see inheritance as a way to do that byt I am not very experienced with templates. - each call to the object must be intercepted: each function to be called must be coded manually (this is not the case in python, you can automatically intercept calls). So you must provide an easy interface to notify that a call was made to a certain function. - every argument to a function called must be stored and compared to what was expected. I don't know if templates can cleverly handles this one. It sounds tricky to me to store and compare objects you do not know beforehand. But as I said, I am in no way a template guy. Using a string representation of the objects to store them and compare them sounds to me like a simple and implementable solution. - store the return value so that it can be returned when the function call is make. That sounds really tricky to me. String serialisation can not handle that. I don't think simple template usage can. Advanced template certainly can however. An idea is emerging on how to do that. What is sure is that implementing this is C++ will be a lot more complicated than in other languages. regards, Philippe |
From: Philippe F. <P....@OB...> - 2002-08-14 16:02:05
|
> Here is a example of a mock object in C++. Notes that it is slighty > different than the java implementation. Compare this to the implementation > of MockTestCase in cppunit test suite and you'll see how much easier it > is... > > class MockProjectBuilder : public AbstractProjectBuilder > , public XtlUt::MockObject > { > public: > /*! Constructs a MockProjectBuilder object. > */ > MockProjectBuilder( const std::string &name ); > > /// Destructor. > virtual ~MockProjectBuilder(); > > virtual void beginGroup( const std::string &groupName ); > virtual void endGroup(); > > virtual void addSourceFile( const std::string &relativePath ); > > void setExpectNoGroup(); > void addExpectedGroupName( const std::string &expectedGroupName ); > void addExpectedEndGroup(); > > void setExpectNoSourceFile(); > void addExpectedSourceFile( const std::string > &expectedRelativePath ); > > void setExpectNoMethodCalls(); > > private: > /// Prevents the use of the copy constructor. > MockProjectBuilder( const MockProjectBuilder &other ); > > /// Prevents the use of the copy operator. > void operator =( const MockProjectBuilder &other ); > > private: > XtlUt::ExpectationCounter _endGroupCalls; > XtlUt::ExpectationStringSequence _addSourceFileValues; > XtlUt::ExpectationStringSequence _addGroupNames; > XtlUt::ExpectationStringSequence _methodCalls; > }; Your example is not exactly crystal clear to me. Tell me if I understood correctly: 1. class ProjectBuilder has methods beginGroup(), endGroup() and addSouceFile() 2. to record the call being made, you use the addActual() function 3. to record the arguments of the functions, you use addActual() on ExpectationStringSequence that are specific to a function. 4. the setExpect stuff is here to match expect vs actual 5. you do not handle return values. If I understood right, your framework does not look very generic to me, and requires many manual calls. I think we can do better. To record the called made to a function, we could add a simple macro just after every function calls. Compilers usually have a __FUNCTION__ that contains the name of the function, so this could be automatic. It would record the function being called. Using a dictionnary behind that, you would be able to store and retrieve specific information about each function call. If we assume all objects can be compared using their string representation, we can store the expected and actual arguments of every function. The only thing I haven't been able to figure out yet is how to handle return values. I'll read some C++ books and see if I can find a solution. regards, Philippe |
From: Baptiste L. <gai...@fr...> - 2002-08-24 06:55:11
|
----- Original Message ----- From: "Philippe FREMY" <P....@OB...> To: <cpp...@li...> Sent: Wednesday, August 14, 2002 5:59 PM Subject: RE: [Cppunit-devel] Mock C++ >[...] > > Your example is not exactly crystal clear to me. Tell me if I understood > correctly: > > 1. class ProjectBuilder has methods beginGroup(), endGroup() and > addSouceFile() Yes. > 2. to record the call being made, you use the addActual() function Indeed. _methodCalls is an ExpectationStringSequence. It expect a specific sequence of string. Since the string added are the method name, a specific sequence of method call can be specified that way. > > 3. to record the arguments of the functions, you use addActual() on > ExpectationStringSequence that are specific to a function. Yes, but that's because my arguments are a single string. I could use an expectation sequence for a specific struct that represents the arguments or one expectation sequence for each argument. > 4. the setExpect stuff is here to match expect vs actual Yes. You use those methods to specify what you expect to happen. > 5. you do not handle return values. In that particular case, I did not have any return values. I don't see a problem handling them. Just like expectation sequence, you store a list of value that you use at a later time (though I have not yet implemented such helper for sequence or associative containers). > If I understood right, your framework does not look very generic to me, and > requires many manual calls. I think we can do better. It is fairly generic: you don't have much restriction in writing your expectation and you can easily add new one. What you probably mean is that it does not automatize the creation of such class. > To record the called made to a function, we could add a simple macro just > after every function calls. Compilers usually have a __FUNCTION__ that > contains the name of the function, so this could be automatic. It would gcc does, but I don't remember VC6 providing that. > record the function being called. Using a dictionnary behind that, you would > be able to store and retrieve specific information about each function call. I could be done, but I have an hard time seing how you would fill that map: you are not in the function and the value returned will unlikely be portable across compiler. Also notes that in the provided example, I was tracking the call sequence for all methods. Sometime you are only interested on some specific method call count. You implement this by using a specific expectation call count for each methods. Also, notes by doing that you're being a lot less generic: you force the user to write mock object in a specific way. > If we assume all objects can be compared using their string representation, > we can store the expected and actual arguments of every function. Urk. Never do that. String representation should only be used for diagnostic purpose, never for 'business' stuff. I actually do that by providing functor (with default) for equality and string conversion. Equality is a very volatile things when writing unit test. Sometime you need strict identity (an object can only be equal to itself), sometime you only need a similarity comparison (the objects have the same caracteristic). > The only thing I haven't been able to figure out yet is how to handle return > values. I would use something close to the expectation sequence, using a getExpectedValue() instead of an addActual() method. > > I'll read some C++ books and see if I can find a solution. > > regards, > > Philippe > |
From: Philippe F. <P....@OB...> - 2002-08-26 11:46:59
|
> > If I understood right, your framework does not look very generic to me, and > > requires many manual calls. I think we can do better. > It is fairly generic: you don't have much restriction in writing your expectation > and you can easily add new one. What you probably mean is that > it does not automatize the creation of such class. Yes, I find it not generic in the sense that you have to write everything yourself. I would like mock to be very easy to use. CppUnit takes something like half an hour to set-up. And it is complicated. Mock is going to be even more difficult. > > > To record the called made to a function, we could add a simple macro just > > after every function calls. Compilers usually have a __FUNCTION__ that > > contains the name of the function, so this could be automatic. It would > gcc does, but I don't remember VC6 providing that. Just checked, it does not. __LINE__ + __FILE__ will do. > > > record the function being called. Using a dictionnary behind that, you would > > be able to store and retrieve specific information about each function call. > I could be done, but I have an hard time seing how you would > fill that map: you are not in the function There would be a record mode and a check mode. In the record mode, all calls are recorded for future reference. In check mode, an exception is raised if the function is not called properly and in the right order. In both case, I am in the function. This is how easymock works I think. TestTree::testVisitTree() { Tree tree; tree.load( "Foo1Foo2Bar1" ); // prepare a tree with two Foo1, Foo2 and Bar1 mock = new MockVisitor(); mock.setRecordMode(); mock.visitFoo( tree.foo1 ); // is recorded for future comparison mock.visitFoo( tree.foo2 ); // is recorded for future comparison mock.visitFoo( tree.bar1 ); mock.setCheckMode(); try { tree.visitTree( mock ); } catch (MockError me ) { // display mock error } } class MockVisitor : Visitor, CppMock { // [...] void visitFoo( Foo * foo ) { NOTIFY_FUNCTION_CALLED; NOTIFY_ARG( foo->toString() ); END_NOTIFY; } void visitBar( Bar * bar ) { NOTIFY_FUNCTION_CALLED; NOTIFY_ARG( bar->toString() ); END_NOTIFY; } }; #define NOTIFY_FUNCTION_CALLED notifyFunctionCalled( __FILE__, __LINE__ ) #define NOTIFY_ARG( foo ) notifyArg( foo ) #define END_NOTIFY() endNotify() class FunctionCall { public: std::string file; long line; std::vector< std::string > args; } class Mock { public: // [...] enum MockMode { RecordMode, CheckMode }; void notifyFunctionCalled( std::string file, long line ) { if (_mode == RecordMode) { _currentCall = new FunctionCall; _currentCall->line = line; _currentCall->file = file; } else { // retrieve FunctionCall object _expectedCall = getCurrentExpectedCall(); // check that this function was expected to be called if ( _currentCall->file != _expectedCall->file || _currentCall->line != _currentCall->line ) { throw MockException( "Unexpected method call" ); } } } void notifyArg( std::string arg ) { if (_mode == RecordMode) { _currentCall->args.add( arg ); // I don't know the syntax but you get the idea } else { if ( _currentCall->getCurrentArg() != arg ) { throw MockException( "Unexpected arg" ); } } } void endNotify() { if (_mode == RecordMode) { // store _currentCall _functionCalls->add( _currentCall ); // I don't know the syntax but you get the idea } else { // increment index so that next call to getCurrentExpectedCall() returns the next FunctionCall } } protected: MockMode _mode; FunctionCall * _currentCall; FunctionCall * _expectedCall; std::map< std::string, int > _functionCallIndex; std::vector< FunctionCall > _functionCalls; }; Of course, the syntax is probably not correct and there are more checks to add. But you get the idea. The advantages with this framework is that it is very easy to set-up, in comparison with yours. One don't need to add one store per function. One could add more methods to tell that a function can be called any number of times, and stuff like that. The drawbacks: - does not handle return value - compare objects with their string representation. I agree it is weak, but I tend to prefer simple implementable stuff than very good but very difficult to implement solutions. This is in the spirit of XP. :-) If the developer is aware that string representation is going to be used, he can provide a sensible one. > Also notes that in the provided example, I was tracking the > call sequence for all methods. Sometime you are only > interested on some specific method call count. You implement this by using a specific > expectation call count for each methods. This would be easy to provide. > > Also, notes by doing that you're being a lot less generic: you force the > user to write mock object in a specific way. Yes, I force the user into a simple mock framework. The problem I have with your framework is that the developer must set-up everything by himself. I already have trouble converting my colleagues to test units. If it is too difficult to use a mock object, they simply won't. Of course, if you do everything yourself, you have more control. > > > If we assume all objects can be compared using their string representation, > > we can store the expected and actual arguments of every function. > > Urk. Never do that. String representation should only be used for diagnostic > purpose, never for 'business' stuff. I actually do that by providing functor > (with default) for equality and string conversion. Equality is a very > volatile things when writing unit test. Sometime you need strict identity > (an object can only be equal to itself), sometime you only need a similarity > comparison (the objects have the same caracteristic). I agree but storing an object of any type for further comparison can't be done in an automated way, as far as I can tell. I want to avoid the user declaring each store for each argument function. string looks to me like a nice way to work this around. I wanted to implement that for my next project but it turns out that I won't need it. So I'll implement it some other day, when I'll need it. By the time, anyone is free and encouraged to pick the idea and code it. Philippe |