|
GeneralDiscussion
A general discussion page
IntroductionPlease leave message for any question or feature request in comments or visit amop google group |
Sign in to add a comment
|
|
Search
|
|
GeneralDiscussion
A general discussion page
IntroductionPlease leave message for any question or feature request in comments or visit amop google group |
Sign in to add a comment
Refining my request for a Set() method, I think there should be 3 variants, depending on the type of the parameter, so if, for example, I have the following method: bool Interface::Method(std::string &str, std::string * pointedStr, char * const buffer) I would like to be able to write: mock.Method(&Interface::Method).Set<0>("First").SetPointer<1>("Second").Copy<2>("Third", strlen("Third") + 1).Will(true); And that will translate to: str = "First"; (*pointedStr) = "Second"; memcpy(buffer, "Third", strlen("Third") + 1; What do you think?
Let me write down your case:
class Interface { public: void Foo( std::string& str , std::string* pointedStr , const char* buffer ); }; TEST(TestMockObjectSet) { mock.Method(&Interface::Foo) .Set<0>("First") .SetPointer<1>("Second") .SetCopy<2>("Third", strlen("Third") + 1) .Will(true); std::string first; std::string second; char buf[sizeof("Third")]; CHECK( ((Interface*)mock)->Foo(first, &second, buf) ); CHECK_EQUAL( "First", first.c_str() ); CHECK_EQUAL( "Second", second.c_str() ); CHECK( memcmp("Third", buf, sizeof("Third") )== 0 ); }I prefer using policy object, like this:
TEST(TestMockObjectSetPolicy) { mock.Method(&Interface::Foo) .Set<0>("First") .Set<1>(Policy::Pointer("Second")) .Set<2>(Policy::Copy("Third", strlen("Third") + 1) ) .Will(true); std::string first; std::string second; char buf[sizeof("Third")]; CHECK( ((Interface*)mock)->Foo(first, &second, buf) ); CHECK_EQUAL( "First", first.c_str() ); CHECK_EQUAL( "Second", second.c_str() ); CHECK( memcmp("Third", buf, sizeof("Third") )== 0 ); }What do you think??
I agree. It opens the door to extensibility. BTW, why is the index a template parameter rather than a normal method parameter in Expect (and consequently in set)? It really slows down the typing. Do you happen to have any idea for the destructor problem?
I just made some test, although i cant find a way to get the destructor address. (in fact, i can, but only the address, what i want is the V-table offset..) but i can find a way to get the operator delete address and v-table offset. so i will implement that in AMOP these days. :)
The destructor and the Verify method just added in the current build (v0.22.1).. Oh, right now is 4:22am in hong kong... i should go to bed :)
This looks great. However, I can't combine the destructor and verify, so `mockStore.Method(amop::Destructor()); mockStore.Verify();` doesn't fail although it should. (since I can't use Wills() or Expects() on the destructor)
You can use the Count function:
Great. Thanks!
Thinking about it, you can also add the option to use a policy object with Expect, not just Set.
That's good idea too. :)
For the coming version v0.23.1 (if not other bugs fix):
Any other idea ?
Currently i have 2 open source projects:
I plan to work on other one, which is about GUI acceptance test for C++, do you have any idea that, or wanna co-operate with me?
Hi, I would love to co-operate in the future, but currently I do not have the time. In the meanwhile I'm happy to contribute from a product management standpoint :)
I think you should stick with the existing projects (maybe adding just one more), and make them best of breed. I really think amop has a great future.
So what is next about amop?
I totally have no idea about the big improvement of it except porting to another platform like linux.
hopefully, I'll have a new idea by the time v0.23.1 is out (most of my suggestion up to now were simply things that I noticed I need while using amop. I'm pretty certain there will be more). It might also be helpful to look at mocking frameworks for other platforms (maybe record-plaback, a mocking repository with a VerifyAll? method that verifies all mocks, etc...).
Another thing that will probably turn up is memory issues (I'm not sure what is the effect of calling delete on the vtable, or what stack corruptions can occur due to the fact that the method signatures in the vtable do not match the original ones). Memory problems are much easier to debug when they reproduce in unit tests, but if amop will have memory issues itself, it will be harder to differentiate.
And of course, don't forget to make sure that it compiles with warning level 4 :) (I think turning static methods into inline will solve it).
http://www.ayende.com/Wiki/(S(4h0bmy551hvxvl45ygwv0fie))/Default.aspx?Page=Rhino+Mocks+Documentation
You can find a demonstration of both features in the introduction.
Oh thanks, let me take a look first:)
Another suggestions: the ability to customize the default function so it will do something else instead of throwing a TNotImplementedException.
Um.. for examples?? what is it used for?
Well, for example, I use CXXTEST, so I might be interested in replacing the default method with TS_FAIL("Method not implemented").
ok, how about this one
TEST(NotImplementRedirect) { struct Local { static void NotImplement() { std::cout << "Not Implement" << std::endl; } } TMockObject<Interface> mock; mock.NotImplemented(&Local::NotImplement); mock.SimpleFunction(); }But.. in fact, if the simple function called, the stack winding will be have some problems...
it need to be tested...
The example looks just what I wanted. As for the stack winding, I don't see the difference between this and calling the original not implemented function, but in any way, this is not an issue since I expect these functions to be called only when tests are failing, whereas memory issues will be investigated when the tests are passing.
Hey. Any news?
Oh, i just ported the source code to support GCC. (it is tough..), And for your feature request, i will add some in the coming weekends... :)
In config.h I see the line #define MAX_NUM_VIRTUAL_FUNCTIONS 25
Looking further, it appears this framework will behave strangely if you happen to have an interface with more than 25 functions. 25 seems to be way too small. If you have GUI objects or others, hundreds of functions in an interface is not unusual.
Is there any real harm changing this variable to 1024?
Do you plan to support the interface keyword? Interfaces are all pure-virtual and cannot have destructor. I know this is MSVC specific.
E.g.
__interface IInterface { virtual void SimpleFunction() = 0; virtual void SimpleFunctionWithParams(float, std::string, const char*) = 0; virtual int SimpleFunctionWithParamsAndReturn(float, std::string, const char*) = 0; virtual void SimpleFunctionWithAlotParams(float, int, float, int, std::string) = 0; virtual int SimpleFunctionWithReturn() = 0; virtual int& SimpleFunctionWithRefReturn() = 0; virtual void SimpleConstFunction() const = 0; virtual std::string ComplexFunction(const std::string& crs, std::string& rs, std::string s) = 0; virtual std::string ComplexConstFunction(const std::string& crs, std::string& rs, std::string s) const = 0; };Hey, I just downloaded the last version, and I'm getting errors in my project:
I fixed the first 3 warning... i will put it in the current dev. build later.
But i tried the #4:
// Bug Test //------------------------------------------------------------------ TEST(MockObjectMethodExpectConstInt) { TMockObject<IInterface> mock; mock.Method(&IInterface::GetInt).Expect<0>(std::wstring(L"Item")).Expect<1>(10).Will(15); CHECK_EQUAL(15, ((IInterface*)mock)->GetInt(std::wstring(L"Item"), 10)); }But i get no warning and error.. can you give me more information??
Hmm, the only thing I can think of is that I'm using VS2003 (I believe you're using 2005).
If it helps, here is the full error from VS:
Comparable.h(63) : error C2166: l-value specifies const object Comparable.h(62) : while compiling class-template member function 'void amop::Detail::TComparableImp<From,To>::Assign(const amop::any &)' with [ From=int, To=const ToType ] Comparable.h(106) : see reference to class template instantiation 'amop::Detail::TComparableImp<From,To>' being compiled with [ From=int, To=const ToType ] ReturnMatchBuilder.h(74) : see reference to function template instantiation 'amop::Detail::TComparable amop::Detail::TComparable::MakeCompare<const ToType,T>(From)' being compiled with [ T=int, From=int ] ReturnMatchBuilder.h(100) : see reference to function template instantiation 'amop::Detail::TComparable amop::TReturnMatchBuilder<F>::SetNormal::Set<1,T>(T)' being compiled with [ F=int (__thiscall Configuration::* )(const std::wstring &,const int), T=int ] Test.h(41) : see reference to function template instantiation 'amop::TReturnMatchBuilder<F> amop::TReturnMatchBuilder<F>::Expect<1,int>(T)' being compiled with [ F=int (__thiscall Configuration::* )(const std::wstring &,const int), T=int ]Hi onfreund, i dont have the 2003 in my hand now, but i guess i know the problem, Please try to download the newer 0.31.2 , and test it ...
And may i have your email??
Hi, really like what I'm seeing. Great idea to use the ABI. Obvious and genius at the same time.
Could you add an operator to select the functions rather than needing a cast. i.e.
template <class T> class TMockObject : public Detail::TMockObjectBase { public: //... T* operator->() { return (T*)GetVptr(); } //...This will enable calls to be made without the cast. e.g.
//------------------------------------------------------------------ TEST(MockObjectMethodMultiWithReturn) { TMockObject<IInterface> mock; mock.Method(&IInterface::SimpleFunctionWithReturn) .Wills(22).Wills(11); CHECK_EQUAL(22, mock->SimpleFunctionWithReturn()); CHECK_EQUAL(11, mock->SimpleFunctionWithReturn()); }Hi pcwood, i am very happy that someone think this library good.
For the -> operator, i thought of it before, but i finally decided that i wont add that. Because the "Real" usage of the TMockObject wont call the method directly. Normally you will call the method by setting the TMockObject inside it and then test it behavior. Like this:
class IPersist { virtual ~IPersist() = 0; virtual bool Write(TFile* file) = 0; }; //------------------------------------------------------------------ TEST(FileWriterTest) { TMockObject<IPersist> mock; File file; mock.Method(&IPersist::Write).Expect<0>(&file).Will(true); file.Write(); }In this test case, the method wont be called directly. So that's why i haven't added the ->operator.
Yes I thought that after I'd posted. It's not needed, only in the test cases for the mock itself. I'll leave the comment in case anyone else thinks the same. Thanks for getting back.
Hmm, I tried and the new version and now I have this: ` Test.cpp(0) : fatal error C1001: INTERNAL COMPILER ERROR
Test.cpp `ICE??...anyway thanks onfreund, i will install a VS2003 and then test it..thanks.
Hi. Did you get a chance to test it?
Sorry, this week i am very busy, i will test it later.
Hi - Any news on a Linux version?
Oh, currently it is supported, please go to BuildInstructions for more details
Thanks. Progressing, but get this error on GCC: "enumerator value for `Result' not integer constant" - as far as I can see on this line: enum { Result = ((sizeof (Convertor(MakeFrom??())) == sizeof(Yes))) };
Could u mind to give me more information about that?? What gcc version you are using ?? and how to that error occur?? from building the code or using AMOP in you own code?
Sure.
gcc version 3.2.3 20030502 (Red Hat Linux 3.2.3-59)
The Code:
#include <stdio.h> #include <string.h> #include <fstream> #include "UnitTest++.h" #include "XmlTestReporter.h" #include "MockObject.h" using namespace amop; class Interface { public: virtual void VoidMethod () = 0; virtual int ReturnIntNoParams () = 0; virtual int ReturnIntWithParams() = 0; }; SUITE(TheTestSuite) { TEST(TheTestCase) { TMockObject<Interface> mock; mock.Method(&Interface::ReturnIntNoParams) .Will(1); } } static void RunUnitTests() { std::ofstream xmlFile("TestResult.xml"); UnitTest::XmlTestReporter reporter(xmlFile); UnitTest::RunAllTests(reporter, UnitTest::Test::GetTestList(), NULL, 0); } int main() { RunUnitTests(); return 0; }Which produces:
../amop/include/ReturnMatchBuilder.h: In instantiation of 'amop::Detail::IsConvertible<int, int>':
../amop/include/ReturnMatchBuilder.h:34: instantiated from 'amop::TReturnMatchBuilder<F> amop::TReturnMatchBuilder<F>::Will(T) [with T = int, F = int (Interface::*)()]' main.cpp:35: instantiated from here
../amop/include/ReturnMatchBuilder.h:34: invalid use of undefined type 'struct amop::Detail::IsConvertible<int, int>'
../amop/include/Comparable.h:13: declaration of 'struct amop::Detail::IsConvertible<int, int>'
../amop/include/ReturnMatchBuilder.h:34: invalid use of undefined type 'struct amop::Detail::IsConvertible<int, int>'
../amop/include/Comparable.h:13: declaration of 'struct amop::Detail::IsConvertible<int, int>'
../amop/include/ReturnMatchBuilder.h:34: enumerator value for 'Result' not integer constant
../amop/include/ReturnMatchBuilder.h: In member function 'amop::TReturnMatchBuilder<F> amop::TReturnMatchBuilder<F>::Will(T) [with T = int, F = int (Interface::*)()]': main.cpp:35: instantiated from here
../amop/include/ReturnMatchBuilder.h:34: creating array with size zero ('-1')
Just tested with gcc version 3.4.6 20060404 (Red Hat 3.4.6-3) and compiles ok.
I just test it in gcc 4.1.3, it is ok too..
Maybe there are some template related feature not supported in the older gcc version.
I am stuck with v3.2.3 though so will have to find a workaround...
I am not a linux guy, so my knowledge of it is very limited, but is it possible install 2 different version in the same environment??
If not possible, is there any free site i can access for test it?
Sorry, don't know of anywhere to test Linux.
Another error I'm getting with GCC 3.2.3:
../amop/source/Comparable.h:138: return-statement with a value, in function declared with a void return type
Thanks, i just fixed in SVN. i will make a release in coming days.
Hi onfreund, i just added an VS2003 project and Solution in the SVN Trunk, and i tested it, all tests can build.
Then could you mind write a simple unittest for the error and post it in here??
Very strange, I can't seem to reproduce it either. It used to happen consistently, but after restarting my computer it's gone. Sorry you went through all this trouble.
Another feature request, I would like to be able to mock throwing an exception, e.g. mockFile.Method(&File::Close).Throws(FileCloseException("message"));
Ok , I just added that in the issue page, i will add that later.
Thanks, I added a comment there.
Is there any way to output the expected and actual values when a comparison fails? It would be nice if the TNotEqualException had string expected and actual values instead of the "any" type so that when the exception is caught its values could be displayed.
Awesome framework by the way!
Hi Vlad3D, that's good idea of adding that strings in the exception, let me add in the issue page.
Oh , sorry, after a deeper thinking, although i can use template class to throw the exception, but you still know the type to catch it, right?
e.g:
try { ((IInterface*)mock)->Foo(1); // It will throw a TNotEqualException<int> } catch( ??? e) { std::cout << e.Actual << std::endl; }The ??? is the type u catch, but if you know the type of the template, how about just casting the "any" to the that type, it is easier, right?
Yeah, I also thought about just outputting the contents of the "any" type, but there could be a problem if the type is a pointer to something on the stack - it might have been destructed by the time the exception is caught. I guess you could make a copy, but that might not work with all types.
I figured that it might be easier to just output the values to a string stream with operator << and then the user could provide user defined operator << for custom types.
Could you mind to write a simple test case which i can follow?
Sure. Here's a possible scenario:
class IBar { public: ~IBar(); void bar(int* pi) = 0; }; class Foo { public: Foo(IBar* pBar) : m_pBar(pBar) {} void foo(int a) { int b = a * a; m_pBar->bar(&b); } private: IBar* m_pBar; }; TEST(FooTest) { TMockObject<IBar> mockBar; Foo testFoo(mockBar); mockBar.Method(&IBar::bar).Expect<0>(Policy::Pointer(81)); try { testFoo.foo(8); } catch (TNotEqualException e) { cout << *any_cast<int*>(e.actual()); // oops, actual value is now gone! } }The other thing is sometimes there are a lot of different parameter types so you'd have to try to cast the any to each of the possible types, but that's not a big issue.
thanks so much, i think i can change the policy for Pointer and other policy , before the exception is throwing, it will copy the value to the exception (not only the pointer ). But i think it cant solve all problems related on that.
On the other hand, i can use the ostream << operator to output the value to exception. But i think it cant solve the problem too, because it will change all value being checked (using the TMockObject::Expect) have to have the ostream << operator...
It is because there are no way to check a type whether is supporting that operator in compile time. (Maybe there are a way to check...i just know how to do it C++0x...:P )
So i have to think deeper on this issue first..Or do you have any other idea on that?
//the base calss class CB { public: CB(); ~CB(); protected: int error; }; class CD:public CB { public: CD(); ~CD(); //result is the real resualt, //the return value is controled by CB::errCode virtual int add(double a, double b, double &result); virtual int mul(double a, double b, double &result); }; //under test method,calculate (a+b)*b + b int complexCal(CD *pCal,double a, double b,double &dResult) { double result; pCal->add(a, b, &result);//a+b if(pCal->error) { return -1;//failed } pCal->mul(result, b, &result);//(a+b)*b if(pCal->error) { return -1;//failed } pCal->add(result, b, &result);//(a+b)*b + b if(pCal->error) { return -1;//failed } dResult = result; return 0; } TEST(testComplexCal) { TMockObject<CD> mock; CD *pCal = (CD*)mock; double a =1; double b = 2; double respectResult = 6; int respectReturn = 0; double realResult = 6; int realReturn = 0; //mock.Method(&calcProxy::add).Will(3); //mock.Method(&calcProxy::mul).Will(6); }Hi wangfeideyouxiang,
1. Currently amop only support pure abstract interface testing, which means the class used in TMockObject<T> have to be no data member. 2. Use Will method will return the default result value, if you want to control the specific values in each time, use Wills instead.
PS: i am Chinese in HK , so you can use chinese in here too:)
1. This is actually a good thing. This could be a sign that you're using inheritance for the wrong reason. 2. I think that for the benefit of others (such as myself), we should stay with English :)
Thanks a lot, now i counter the same problem as Vlad3D because I want to control the value of result in function add. PS:i am in shenzhen_.
the problem of Vlad3d is getting the value of the input argument. But what is your case ?? can you write me down a sample test case ??
Hi, I m having difficulty in using AMOP with COM style interfaces. Suppose the following is the interface
struct declspec(uuid("793DAC59-430B-4A44-AE80-36C2E0A0B507")) declspec(novtable) IView : public IDispatch { public:
};and when I create a mock like this -
I get compilation errors which goes like - error C2027: use of undefined type 'amop::Detail::Functor<T>' with [
]and it goes on like that. I have been able to isolate the error to the calling convention. If I remove STDMETHODCALLTYPE, everything works fine. Is there any work around to this ? I tried compiling with /Gz to use the stdcall calling convention, but that did not work. So is there anyway to make this work?
Thanks for your help Sriram
That did not show up properly. Here is the formatted post Hi, I m having difficulty in using AMOP with COM style interfaces. Suppose the following is the interface
struct __declspec(uuid("793DAC59-430B-4A44-AE80-36C2E0A0B507")) __declspec(novtable) IMyInterface : public IDispatch { public: virtual HRESULT STDMETHODCALLTYPE DisplayMessage(BSTR message) = 0; };and when I create a mock like this -
TMockObject<IMyInterface> mock; mock.Method(&IMyInterface::DisplayMessage);I get compilation errors which goes like -
d:\arena\amop\source\FunctionHolder.h(18) : error C2027: use of undefined type 'amop::Detail::Functor<T>' with [ T=long (__stdcall IMyInterface::* )(BSTR) ] d:\arena\amop\source\CallHandler.h(25) : see reference to class template instantiation 'amop::Detail::FunctionHolder<I,T>' being compiled with [ I=0, T=long (__stdcall IMyInterface::* )(BSTR) ] d:\arena\amop\source\CallHandler.h(23) : while compiling class template member function 'amop::Detail::TFunctionAddress amop::Detail::CallHandler::SelectID<I>::Get<T>::Select(size_t)' with [ I=0, T=long (__stdcall IMyInterface::* )(BSTR) ] d:\arena\amop\source\CallHandler.h(36) : see reference to class template instantiation 'amop::Detail::CallHandler::SelectID<I>::Get<T>' being compiled with [ I=0, T=long (__stdcall IMyInterface::* )(BSTR) ] d:\arena\amop\source\CallHandler.h(42) : see reference to function template instantiation 'amop::Detail::TFunctionAddress amop::Detail::CallHandler::Select<T>(size_t)' being compiled with [ T=HRESULT (__stdcall IMyInterface::* )(BSTR) ] d:\arena\amop\source\MockObjectBase.h(92) : see reference to function template instantiation 'amop::Detail::TFunctionAddress amop::Detail::CallHandler::Create<F>(size_t)' being compiled with [ F=HRESULT (__stdcall IMyInterface::* )(BSTR) ] d:\arena\amop\source\MockObject.h(30) : see reference to function template instantiation 'amop::TReturnMatchBuilder<F> amop::Detail::TMockObjectBase::CreateMatchBuilder<F,T>(F)' being mpiled with [ F=HRESULT (__stdcall IMyInterface::* )(BSTR), T=IMyInterface ] d:\arena\amop\source\Test.h(25) : see reference to function template instantiation 'amop::TReturnMatchBuilder<F> amop::TMockObject<T>::Method<HRESULT(__stdcall IMyInterface::* )(BSTR)>)' being compiled with [ F=HRESULT (__stdcall IMyInterface::* )(BSTR), T=IMyInterface ] d:\arena\amop\source\FunctionHolder.h(18) : error C2146: syntax error : missing ',' before identifier 'ReturnType' d:\arena\amop\source\FunctionHolder.h(18) : error C2065: 'ReturnType' : undeclared identifier d:\arena\amop\source\FunctionHolder.h(18) : error C2955: 'amop::Detail::IsEqual' : use of class template requires template argument list d:\arena\amop\source\Config.h(33) : see declaration of 'amop::Detail::IsEqual' d:\arena\amop\source\FunctionHolder.h(19) : fatal error C1903: unable to recover from previous error(s); stopping compilationCurrently AMOP only support cdecl... Um.. let me try your test-case first, and see there are any solution about that..
Let me know if I can help in any way. Thanks for your help.
I added your test-case and i try to find a workaround, but AMOP depend on cdel behavior (i.e. caller clean up the stack) to find out the offset of a function. (it is the main magic of AMOP no-interface implementation mechanism.) So it is hard to support stdcall...
But..here is a fast hack... just define this in the beginning of your test-case .cpp
It will work like a magic :)
Perhaps, I should have provided a better example. I have already tried (#define STDMETHODCALLTYPE ) doing this. It works for the example that I provided. Not for the actual project as the interface definition in the actual project comes from a auto generated .h file from MIDL compiler. And not only that, since there are lots of ATL base classes involved, the #define has to preclude any header includes. I tried this just now once again and I am seeing lots of errors due to calling convention mismatch. In short, it is not easily possible for me to go this route.
I do understand the need for cdecl. So right now, i m using poor man's mock. Hopefully we can come up with some solution.
Now, I want to mock function SimpleFunctionStuct, but whose parameter PC don't implement the == operator, I cann't control the code of PC, because it is generated automatically. how I can test this case with AMOP, thank you~~~ class PC { public: int i; public: //bool operator==(const PC& oc)const //{ // return true; //} }; virtual void SimpleFunctionStuct(PC *p) = 0; //------------------------------------------------------------------ TEST(MockObjectMethodSimpleFunctionStuct) { TMockObject<IInterface> mock; PC op; mock.Method(&IInterface::SimpleFunctionStuct) //.Set<0>(Policy::Pointer(op));//failed to compile, must implement == .Set<0>(&op);//it will crash PC op2; ((IInterface*)mock)->SimpleFunctionStuct(&op2) ; }How about a free function version of operator==??
bool operator==(const PC& pc1, const PC& pc2) { return True?? }I have solve the problem, because i didn't initialize the variable i; PC(){ i = 0;}; now i have anther problem, whether it effect the test result if i implement operator== simplely?the == operator is used for checking whether 2 objects are equal. In your case, you should check whether op2 are equal to op.
I will suggest a rewrite of the MockObjectMethodVerifyExpects? test
to
mock.Method(&IInterface::SimpleFunctionWithParams) .Expects<0>(1.0f).Expect<1>("").Expect<2>("") .Expects<0>(2.0f) .Expects<0>(3.0f);This places the arguments in the order they are later seen in the code, which makes the test more instructive as example.
I am trying to mock an call to operator(), but then it crashes.
I am extending the IInterface with
I am testing this with:
TEST(MockObjectOperatorVerifyExpects) { TMockObject<IInterface> mock; mock.Method(&IInterface::operator()) .Expects<0>(1).Expect<1>(2) .Expects<0>(10).Expect<1>(20) ; IInterface & callFace(*((IInterface*)mock)); callFace(1, 2); callFace(10, 20); // It will not throw mock.Verify(); callFace(100, 200); CHECK_THROW(mock.Verify(), TCallCountException); }This is on GCC 4.2.1
descartes, if you are looking for shell acconts to build/test on Linux, you should consider sourceforge, they have buildfarms for many platforms.
Hello, I'm starting with amop(Newb), but I have some difficults to understand. Can you add comments to explain the utility of each method? Is it possible to mock a class with implementation(no abstract class)? Is it possible to mock the class tested? Thank you!
hi j...@gavia.dk, it is not a bug in amop, the current test case should be like this:
//------------------------------------------------------------------ TEST(MockObjectOperatorVerifyExpects) { TMockObject<IInterface> mock; mock.Method(&IInterface::operator()) .Expects<0>(1).Expects<1>(2).Will(0) .Expects<0>(10).Expects<1>(20).Will(0) ; IInterface & callFace(*((IInterface*)mock)); callFace(1, 2); callFace(10, 20); // It will not throw mock.Verify(); callFace(100, 200); CHECK_THROW(mock.Verify(), TCallCountException); }Because your operator have return value, you have to tell TMockObject to return some value by using will.
And TMockObject::Expects and TMockObject::Expect cannot be used at the same time.
hi eaimounir,
1. Basically, i want to make some tutorial of whole library, but there are some difficults for me, the main part is my english is bad and this project is my spare time project... but i will try to write some tutorial in coming. i promised. ;)
2. AMOP is only used for pure abstract class now. Although i am thinking of make it work in partially with implementation. But right now, the answer is no, sorry.
Thanks a lot descartes, now I have changed the test to
TEST(MockObjectOperatorVerifyExpects) { TMockObject<IInterface> mock; mock.Method(&IInterface::operator()) .Expects<0>(1).Expects<1>(2).Will(142) .Expects<0>(10).Expects<1>(20).Will(242) ; IInterface & callFace(*((IInterface*)mock)); CHECK_EQUAL(142, callFace(1, 2)); CHECK_EQUAL(242, callFace(10, 20)); // It will not throw mock.Verify(); callFace(100, 200); CHECK_THROW(mock.Verify(), TCallCountException); }But this fails with Failure in MockObjectOperatorVerifyExpects: Expected 142 but was 242. It seems like it is only recording the last "Will" value. I have even tried to split the two expectations (including Will) into two statements, but that didn't change anything.
BTW: It would be nice if AMOP could give a decent error message, when Expect was used opposed to Expects+Will, in stead of crashing.
Jarl
I changed the mock statement from
mock.Method(&IInterface::operator()) .Expects<0>(1).Expects<1>(2).Will(142) .Expects<0>(10).Expects<1>(20).Will(242) ;to
mock.Method(&IInterface::operator()) .Expects<0>(1).Expects<1>(2).Wills(142) .Expects<0>(10).Expects<1>(20).Will(242) ;And that made it work. Could you add this test case to the file TestMockObject?.cpp? Primarily because it will serve as documentary example.
Again, if I change both Will to Wills, it crashes.
It is not very natural to use Expects versus Expect and Wills versus Will just because it is not the last mock call, and if you pick the wrong one it either does not behave as expected or it crashes.
Is there anyway that the API could be changed to only have one method in stead of two methods depending on whether something is comming after or not? I imagine that the introduction of two methods Expects and Expect and similar for Will(s) is due to some technical coding issues, not because it makes the API more intuitive.
What are the coding challenges in providing such an API with only one method named Expect and Will respectively?
Could you elaborate a bit on this descartes?
Jarl
Hi Jarl, 1. i totally agree that Expect & Expects and Will & Wills is mis-leading. My original ideal is something like this
// For all call mock.Method(&IInterface::operator()) .Expect<0>(1).Expect<1>(2).Will(142); CHECK_EQUAL(142, callFace(1, 2) ); CHECK_EQUAL(142, callFace(1, 2) ); CHECK_EQUAL(142, callFace(1, 2) ); mock.Clear(); // For each calls mock.Method(&IInterface::operator()) .Expect<0>(1,2,3).Expect<1>(2,4,6).Will(142, 143,144); CHECK_EQUAL(142, callFace(1, 2) ); CHECK_EQUAL(143, callFace(2, 4) ); CHECK_EQUAL(144, callFace(3, 6) );It make all things clear, right? but when i try following test-case:
// Just for one time!!! mock.Method(&IInterface::operator()) .Expect<0>(1).Expect<1>(2).Will(142); CHECK_EQUAL(142, callFace(1, 2) ); // It will not throw mock.Verify(); callFace(100, 200); // I want it will throw, but this test will fail.. CHECK_THROW(mock.Verify(), TCallCountException);The problem here is , i dunno what i should use for when the "each call" version only for one time. So i add the Expects and Wills version. The other method for this problem in other Mock Library is doing something like:
// For all call mock.Method(&IInterface::operator()) .Expect<0>(Default(1)).Expect<1>(Default(2)).Will(Default(142));But i think the "for all call" version is more often then the "for each all" one. So i chose current form. But if you have any better idea, pls tell me.
2. >>> BTW: It would be nice if AMOP could give a decent error message, when Expect was used opposed to Expects+Will, in stead of crashing. >>>
Um... In fact, it is not crashing... it just throw an exception. It is better to make the exception inherit from std::exception to let UnitTest?++ to show that.
3. The exception thrown when you use both wills, is because the Wills only handle each calls, means when you call callFace(100, 200), there are no suitable return value for that call, so it throw a TCallCountException.
It make all things clear, right?
I understand what you mean. In the first case ("for all call") it shall mock and verify arguments an infinite number of times, and shall not care about number of calls, whereas in the other case ("for each call") the number of calls must match exactly, and each call has distinct arguments that it shall verify against, and distinct return values it shall mock.
However, the syntax for multiple "for each call" as you show
// For each calls mock.Method(&IInterface::operator()) .Expect<0>(1,2,3).Expect<1>(2,4,6).Will(142, 143,144); CHECK_EQUAL(142, callFace(1, 2) ); CHECK_EQUAL(143, callFace(2, 4) ); CHECK_EQUAL(144, callFace(3, 6) );is not at all natural/intuitive. First of all the numbers are read in different order than they will show up in client code calling the mock object, this is also mentioned in my comment May 30, 2008.
I will suggest that both the "for all call" situation and the "for each call" situation all takes exactly one argument (no more), this will of course force the use of "for each call" into code like this:
// For each calls mock.Method(&IInterface::operator()) .Expect<0>(1).Expect<1>(2).Will(142) .Expect<0>(2).Expect<1>(4).Will(143) .Expect<0>(3).Expect<1>(6).Will(144); CHECK_EQUAL(142, callFace(1, 2) ); CHECK_EQUAL(143, callFace(2, 4) ); CHECK_EQUAL(144, callFace(3, 6) );but that is a huge readability advantage, each line of mocking correspond to each call done later on the mock object.
This makes the mock code much more clear. Now every use of "for each call" mocking is now in the problematic category as you mention: i dunno what i should use for when the "each call" version only for one time
And to that I will suggest the following differentiation. In the case "for each call" I will keep a syntax similar to the one just illustrated. But to mock "for all call" I will suggest the following syntax:
// For all call mock.All(&IInterface::operator()) .Expect<0>(1).Expect<1>(2).Will(142); CHECK_EQUAL(142, callFace(1, 2) ); CHECK_EQUAL(142, callFace(1, 2) ); CHECK_EQUAL(142, callFace(1, 2) );The differentiation is now made very clear very early in the code.
Regards implementaiton. There are several options for implementing this. Either the return type TReturnMatchBuilder<F> could be extended with an extra bool template parameter differentiating the two ways to mock, providing a compile time different implementation (fastests), or TReturnMatchBuilder<F> could contain a bool member indicating the difference. This will make the distinction at runtime.
Back to useage syntax, now that I think about it, I may even prefer the following even more verbose distinction between the two ways to mock:
// For all call mock.All(&IInterface::operator()) .Expect<0>(1).Expect<1>(2).Will(142); CHECK_EQUAL(142, callFace(1, 2) ); CHECK_EQUAL(142, callFace(1, 2) ); CHECK_EQUAL(142, callFace(1, 2) ); mock.Clear(); // For each calls mock.Single(&IInterface::operator()) .Expect<0>(1).Expect<1>(2).Will(142) .Expect<0>(2).Expect<1>(4).Will(143) .Expect<0>(3).Expect<1>(6).Will(144); CHECK_EQUAL(142, callFace(1, 2) ); CHECK_EQUAL(143, callFace(2, 4) ); CHECK_EQUAL(144, callFace(3, 6) );BTW: In other literature the "for all call" mock object is called a stub, where as the "for each call" mock object is called a mock object.
Jarl
descartes writes:
Yes, that will definitely be an improvement.
If you plan to change the API as suggested, I will further encourage you to rename Will/Wills to Return for two reasons:
Jarl
It is what i want!!
I will changed the API as your suggestion in 4.0. And the old one will be abandoned.
On the other hand, i think the documentation of AMOP need to be have a big improvement too...
I even think that if you add the extra bool template parameter to TReturnMatchBuilder<F> to diffenrentiate the return types of All() and Single(), it must be possible to make compile-time errors for invalid constructs such as
mock.All(&IInterface::operator()) .Expects<0>(1).Expects<1>(2).Returns(142) .Expects<0>(10).Expects<1>(20).Returns(143) ;or (Count should only be valid on type returned by All, not Single)
mock.Single(&IInterface::operator()) .Count(3);And even enforce correct parameterorder such that the following construct becomes invalid (and gives compile error):
mock.All(&IInterface::operator()) .Expects<1>(2).Expects<0>(1).Returns(142) ;I would even like to see the framework support the following syntax:
mock.All(&IInterface::operator()) .Expects(1,2).Returns(142);in stead of
mock.All(&IInterface::operator()) .Expects<0>(1).Expects<1>(2).Returns(142);because that is how other mock frameworks work, and recognition makes the learning much easier.
Further, what do you think of changing the API for querying information on the mocked methods? That is, changing
to
So there will then be three main methods on MockObject?:
>>> I will changed the API as your suggestion in 4.0
You probably mean version 0.4
>>> On the other hand, i think the documentation of AMOP need to be have a big improvement too...
You should really consider Doxygen for making a API reference, anyway I can maybe help writing a tutorial similar to TestMockObject.cpp but more verbose for version 0.4
Jarl
Amop produces some minor warnings when compiling with all warnings on GCC. I would like to contribute a patch to fix this. How is this best done?
Jarl
1. Yes, version 0.4 :) 2. I like the Query one 3. You can post the patch in google group, or email me. thanks a lot.
Hi,
I can't seem to get the exception mocking to work. I'm trying both Throw() and Throws but none of them causes the mock to throw any exception (the scenario is equivalent to the simple test case and I'm using VS2003).
But i tried that in my VS2003 and it works (at least it works in the test case in testMockObject)
Hi, I want to test a inferface that contains overload fuction as follows, it cannot compile, thank you. virtual int f1(int iTypeId, int a) { return 0; } virtual int f1(int iTypeId, int a, int b) { return 0; } \mytest.cpp(227) : error C2228: “.Set”的左侧必须有类/结构/联合类型 \mytest.cpp(228) : error C2228: “.Set”的左侧必须有类/结构/联合类型 \mytest.cpp(229) : error C2228: “.Will”的左侧必须有类/结构/联合类型Hi wangfeideyouxiang, could you mind to write down your whole test case ?
class MyInterface { public: virtual int f1(int iTypeId, int a); virtual int f1(int iTypeId, int a,int c); virtual ~MyInterface(){} }; TEST(MyInterface_F1) { TMockObject<MyInterface> mock; MyInterface *p = (MyInterface *)mock; mock.Method(&MyInterface::f1) .Set<0>(1) .Set<1>(2) .Will(3); int iTypeId = 22,a=33,iRet=0; iRet =p->f1(iTypeId,a); }See:
http://groups.google.com/group/amop-group/msg/0a2ed8152049eaa1
I'm failing this simple test:
void testSimpleThrow( void ) { amop::TMockObject<File> mock; mock.Method(&File::Delete).Throws(std::bad_exception("test")); TS_ASSERT_THROWS(((File *)mock)->Delete(), std::bad_exception); }What am I doing wrong?
oic, i find that part has a bug when your function (i.e. File::Delete) return type is void, the exception will not be thrown. We should fixed that in later release.
OK. Thanks.
Is there a way to mock an overloaded function?
For instance, if I have:
class IInterfaceWithOverload { public: virtual void SimpleFunction( int integerInput ); virtual void SimpleFunction( double floatInput ); } void testOverloadedFunction( void ) { amop::TMockObject<IInterfaceWithOverload> mock; mock.Method( &IInterfaceWithOverload::SimpleFunction ) .Expects<0>( 12 ); mock.Method( &IInterfaceWithOverload::SimpleFunction ) .Expects<0>( 23.9 ); mock.Verity(); }This doesn't compile for me. Is there another way to specify the method to call or is there no way to do this with the framework?
BTW, I've had a lot of success with the framework so far. It is excellent.
Note in the above comment I meant to make the two SimpleFunction? declarations as pure virtual as in:
Hi tonylambert:
Yes, See:
http://groups.google.com/group/amop-group/msg/0a2ed8152049eaa1
And Thanks :)
I've made a small change to amop 0.31.3. A patch can be retrieved from:
http://quetzalcoatlus.fahller.se/amop.patch Feel free to use it if you like it.
The change is that the amop exceptions inherit from std::exception, and have an informative what() string. The reason for this change is better error pinpointing. For parameter mismatches, the what() string includes the actual and expected value, if the types support streaming to std::ostream&. If the type does not support it, the what() string only includes which parameter number failed. 3 test cases are added. The code is only tested on GCC.
FWIW, I think it'd be advantageous if the files in include/ weren't duplicated under source/. It's mighty bothersome the way it is.
Example test code:
TEST(MockObjectExpectMismatchWhatString) { TMockObject<IInterface> mock; mock.Method(&IInterface::SimpleFunctionWithParams) .Expect<0>(0.0) .Expect<1>("Correct String") .Expect<2>("Other String"); std::string s; try { ((IInterface*)mock)->SimpleFunctionWithParams(0.0, "Wrong String", "Other String"); } catch (std::exception &e) { s = e.what(); } CHECK_EQUAL("Parameter not equal exception:" " Expect<1>(Correct String) was called with (Wrong String)", s); }Thanks so much, i will patch this later , but in the current svn one, If you are interest to help development AMOP, please join the amop google group or send me email directly. http://groups.google.com/group/amop-group?hl=en&pli=1