-
Notifications
You must be signed in to change notification settings - Fork 68
Home
Whether the syntax is the one you prefer is a personal choice, for which we have a comparison. Whether it can work on your platform is a technical issue, for which we have an overview.
In creating Hippo Mocks we took the syntax of Rhino Mocks as a guideline for a clear and readable syntax. We chose a different name from Rhino Mocks as we're not associated with Ayende (the author of ) and it would be an infringement on the name of Rhino Mocks to pretend to have something directly to do with it. On the other hand, we do have the same approach to the same problem, resulting in much the same solution. In an analogy to CppUnit with JUnit, we chose a relatable but different name.
There does not appear to be an easily usable mocking framework for C++ that allows you to instantiate derived types whilst making your test, without explicitly writing a few classes to do so. As this is prohibited by the language itself, it's understandable. It still is a much better way to do mocking and since it is technically possible (at least in Visual Studio and GCC), there should be a proper framework for doing so.
Since mocking frameworks all differ in syntax and thought patterns for use, you will find one of many to be most suitable for you, and not necessarily the one we made. Other mocking frameworks you may want to consider:
There are a few reasons why you may prefer Hippo Mocks:
- There are multiple developers maintaining it
- It is a single header file which makes including it in your project trivial
- The syntax matches the way you think about mocks, with very few compromises
- It has unit tests itself that run both on GCC (tested with at least 3.4 and 4.3) and on Visual Studio (tested with 2008 / 9.0)
- It is full featured and supports everything we have used so far when mocking, which includes a few features not found elsewhere
- Only found combined in Hippo Mocks:
- 1024 virtual functions per object (up to, that is)
- 16 arguments that are type-safely handled (or infinite, if you use the C++0x version)
- Type-safe return values
- Type-safe exceptions
- Arbitrary order specifications, with syntax limitations to prevent (and explain to you) loops in the call requirements you're trying to specify
- Callback-style replacements for the complex or unspecifiable cases
- Purely constructive syntax - if you don't need a given aspect of a call, you don't specify it.
- Single header file - no library logic, no duplicate definitions.
- Should we use a mocking framework
- When to use mocks
- Mock roles, not objects [PDF]
- Using mock objects effectively
Our opinion is that you use mocks to "fake" or hide the rest of your system in the same way you use headers to hide the knowledge of the implementation of classes. You would then also use mocks every time you use headers - IE, around the borders of each class under test. This makes the test rely solely on the behavior of the class you're testing. That means that when the test breaks, only the class under test needs to be considered.
We are Mike Looijmans and Peter Bindels, both working at Topic Automatisering. We decided that replacing functionality for the purposes of testing easily should be a base construct that is publicly available to everybody. Hence we created Hippo Mocks.
We've decided to release it under the LGPL license. This license, in short, allows you to use it like the GPL would, with the exception that your software does not become GPLed by doing so. You are still strongly encouraged to send back changes you make.
Say, we have a class called Foo.
class Foo {
private:
IBar *bar;
public:
Foo(IBar *bar);
int a();
};
We want to test the a() function. It uses an object with the interface IBar.
class IBar {
public:
virtual ~IBar() {}
virtual void b() = 0;
virtual int c(std::string) = 0;
};
It is some kind of interface crucial to the function of the a() function in Foo, but we really would rather not use the real Bar objects because:
-
It is large
-
It is slow
-
It is not tested
-
It uses external stuff (files, databases, network)
-
It does not exist yet
-
It costs money to use (thanks to Daniel Meyer and his blog for the idea)
-
It is not relevant to Foo's internal logic and to its tests
So, we want to make a test for Foo without using Bar objects. We can use anything instead of Bar that satisfies the IBar interface, so we create a mock repository and tell it to give us a IBar implementing object of otherwise unspecified contents:
void TestAFunctionInFoo() {
MockRepository mocks;
IBar *barMock = mocks.InterfaceMock<IBar>();
Now we have an IBar and we can create the Foo to use it:
Foo *newFoo = new Foo(barMock);
We know that Foo doesn't use its IBar during it's construction. Now, we register the expectations with the mock repository, so it knows what to expect. If we call something we didn't mention before, we get a regular exception informing us of that. If we on the other hand call something we did mention before, it can be checked for validity. We expect a call on b().
mocks.ExpectCall(barMock, IBar::b);
We also expect a call on c(), with hello as argument, upon which it should return 42. Let's inform the mock repository of this information:
mocks.ExpectCall(barMock, IBar::c).With("hello").Return(42);
That reads a lot like the sentences you just read describing what we were going to do - actually, you can read this out loud as regular text. The mock repository arms all expectations immediately, so we can now execute the test itself and let it call the mock functions.
newFoo->a();
We close up the test case, letting the mock repository clean up all the mocks and related stuff:
delete newFoo;
}
This last line (the accolade) tells the mock repository to verify its expectations. If an error already occurred, it will not throw a second exception.
Notice that in the process of making this test, we don't know Bar at all. Bar might not exist, might be unusable or might be developed on the other side of the planet. We have been able to test our code, without knowing Bar or anything about Bar except that it implements IBar.
This completes the first bit of the tutorial. To make this compile, include and include hippomocks.h, and it will compile and work on at least GCC and Visual Studio.
We can mock more than just functions with return values, that have to be called, and that always execute in the same order. The basic call:
mocks.ExpectCall(barMock, IBar::c).With("hello").Return(42);
We can tell the mocking framework a function does not return a value but throws an exception:
mocks.ExpectCall(barMock, IBar::c).With("hello").Throw(std::exception());
We can tell it that a function may be called, but doesn't have to be called:
mocks.OnCall(barMock, IBar::c).With("hello").Return(42);
Maybe we don't care about the arguments to a function
mocks.ExpectCall(barMock, IBar::c).Return(42);
Maybe we want to substitute the entire function body?
int someOtherFunction(std::string);
mocks.ExpectCall(barMock, IBar::c).With("hello").Do(someOtherFunction);
What if we expect multiple, different calls?
mocks.ExpectCall(barMock, IBar::c).With("hello").Return(42);
mocks.ExpectCall(barMock, IBar::c).With("world").Return(42);
And if we don't care about the order?
mocks.autoExpect = false;
mocks.ExpectCall(barMock, IBar::c).With("hello").Return(42);
mocks.ExpectCall(barMock, IBar::c).With("world").Return(42);
And what if we do care about the order, but only between certain function calls? (yes, we're going to make this hard)
mocks.autoExpect = false;
Call &callOne = mocks.ExpectCall(barMock, IBar::c).With("hello").Return(1);
Call &callTwo = mocks.ExpectCall(barMock, IBar::c).With("world").Return(2);
mocks.ExpectCall(barMock, IBar::c).After(callOne).With("1").Return(3);
mocks.ExpectCall(barMock, IBar::c).After(callTwo).With("2").Return(4);
When we have a class that has members, how do we make sure it's constructed?
Bar *barMock = mocks.ClassMock<Bar>();
And when Bar has both pure virtual functions and members?
// there's a limit in the language that disallows us from making a mock of this, as we can't call the constructor and we can't skip calling the constructor
// The only solution at the moment is to derive a class from it that implements the pure virtuals and then to make a mock out of that. The implementation
// will not be used at all, ever, except for calling the constructor.
class BarImpl : public Bar {
public:
virtual void b() {}
};
Bar *barMock = mocks.ClassMock<BarImpl>();
What if we want to change the way you compare types? Well... overload operator== for the type. If that's not possible (example: compare strings with strcmp) use this:
// put this after including hippomocks.h, or modify hippomocks.h
template <>
struct comparer<const char *>
{
static inline bool compare(const char *a, const char *b)
{
return strcmp(a, b) == 0;
}
};
What if we want to change the way you print types? Well... overload operator<< for ostream& and your type and make it output the right way. If that's not possible (example: char type as int):
// put this after including hippomocks.h, or modify hippomocks.h
template <>
struct printArg<unsigned char>
{
static inline void print(std::ostream &os, unsigned char arg, bool withComma)
{
if (withComma) os << ",";
os << (char)arg;
}
};
What if we want to indicate a function is not called, for example after using another function?
// For NeverCalls all the same restrictions and combinations hold that you get with ExpectCall. They're tried after ExpectCalls and before OnCalls.
Call &callOne = mocks.ExpectCall(barMock, IBar::c).With("hello").Return(1);
mocks.NeverCall(barMock, IBar::c).With("1").After(callOne);
Change the following defines to a smaller number before including hippomocks.h (either before including it or on the command-line):
EXCEPTION_BUFFER_SIZE : The maximum size of the message in an exception.
This defaults to 64k, which is smallish on Windows-based platforms and just about always enough.
Values between 1 and 4 kilobytes are probably sufficient for embedded platforms.
VIRT_FUNC_LIMIT : The maximum amount of virtual functions in a class. This defaults to 1024.
Most designs will remain below 16 functions per class or so, with some exceptions going up to 50-100 per class.
If you know your own limit you can reduce this by a lot, saving memory for each mock object created.
Turn on optimizations. Lots of code in Hippo Mocks is suited for inlining to save space.
What if I have two functions with the same name, but a different signature?
Add Overload to the type of registration and cast your function name to the right type. For example, adding:
virtual int c(int) = 0;
to IBar breaks the mocking of c, as it doesn't know which to take. By changing the ExpectCall to:
mocks.ExpectCallOverload(barMock, (int(IBar::*)(int))&IBar::c).With("hello").Return(42);
the compiler is satisfied again. Note that the type for the member needs to be entirely specified and that the ampersand in front of the name is required.