CxxTest / cxxtest

CxxTest Unit Testing Framework
http://cxxtest.com
GNU Lesser General Public License v3.0
166 stars 79 forks source link

Does anyone know if CxxTest could mock a class or not? #52

Open kfeng opened 12 years ago

kfeng commented 12 years ago

Say I have a class CMyClass and a member function request_external_resource():

class CMyClass { ... public: Resource request_external_resource(); ... };

Now I want to mock this class and the member function so that I could return a mockery Resource object. Is Cxxtest capable of doing this? I've checked the user guide and the Mock.h but it seems that they are all about mocking a global/static member function. Very appreciate it if someone could shed a light on this issue, thanks a lot!

moorec commented 12 years ago

I would love to know the same thing! Unfortunately, I'm assuming that the only approach is to perform significant redesign of the code under test to support dependency injection. Ideally, it could let us "flip the switch" between mocks and reals in the same manor as globals.

Actually, I just found this example mixing gmock with CxxTest: http://www.mantidproject.org/Mocking

Perhaps this capability was left out because gmock supports CxxTest unit tests? Here's a link to some gmock documentation: http://code.google.com/p/googlemock/wiki/ForDummies#Using_Google_Mock_with_Any_Testing_Framework

moorec commented 11 years ago

In case others may find this useful, I'll talk about a few things that I've experimented with regarding mocking more than just global or static member functions.

First, as far as inserting replacement classes or objects, the best bet for doing this is by refactoring to support dependency injection (DI). For example, if a class's behavior depends on another class stored as a member, the member object could be supplied to the owning class's constructor. This allows you to provide the production instance of the dependency (often as a defaulted argument) and inject your test instance easily. Similarly, another approach to do this that allows the binding at compile-time is via policy-based class design as discussed in Alexandrescu's Modern C++ Design.

As for (non-static) member function mocking, the most convenient way to do this is by making the function virtual and using inheritance to create a test instance with the intended behavior. If the class is not polymorphic, then maybe it should be to support this kind of testing.

On the other hand, if the type is not polymorphic, and cannot be due to memory or efficiency concerns, consider making the function static. The trick here is to pass it the "this" pointer/reference explicitly as an argument rather than letting it happen implicitly like before. This preserves member access just like before and the implementation can access the same information after refactoring it to use the obj.* or obj-> etc.. Then the CxxTest mechanism can be applied to the function.

Lastly, the order of library inclusion and/or the LD_PRELOAD trick can be used to completely replace dependencies with complete test versions. I've found this approach problematic in two ways. First, it creates duplicated test code that grows the codebase and must be maintained for the life of the project. Second, there is no way to "scope" the replacements beyond the module level. That is, if you replace fprintf for some reason, gcov builds are now broken because they can't write their data files to disk. If you replace it, you replace it everywhere. These two issues have led me to abandon this approach altogether. I strongly recommend avoiding it.

Hope this helps.

tomalakgeretkal commented 10 years ago

Mocking your own objects (as opposed to those found in the runtime, or in system libraries, for which this technique would not be possible, hence the T:: approach for those) is achieved by #includeing the class definition as usual, but linking in alternative definitions for the class function definitions.

$ ls
class.h class.cpp main.cpp test-main.h mock-class.cpp
$ g++ myProgram.cpp class.cpp -o normalProgram  # normal build
$ cxx-testgen -o test-class.cpp test-class.h --error-printer
$ g++ test-class.cpp mock-class.cpp -o testProgram  # test build with mocked `class`
moorec commented 10 years ago

It's been a while since those old posts. Let me follow-up a bit... The approaches using LD_PRELOAD and linking with an alternative set of definitions are called a "link seam." (A seam being the point where the code logically forks between test and production implementations.)

The preferred type of seam, in C++, is an "object seam." That is, to leverage runtime polymorphism to override the production behavior in a test object. This, of course, requires some of the basics of (good) object oriented design.

Recommended reading: Working Effectively with Legacy Code by Michael Feathers