-
Notifications
You must be signed in to change notification settings - Fork 67
Documentation
Hippomocks is C++ mocking framework. It is released on LGPL licence.
To get most up to date version of Hippomocks go to:
https://github.com/dascandy/hippomocks
or
git clone https://github.com/dascandy/hippomocks.git
If you aren't quite sure about mocking and why to use it, you may want to start here: Wikipedia Mock objects
To get Hippomocks to work, all you need to do is include the single "Hippomocks.h" header file in your project.
A few other tidbits: NOTE: When mocking tests, the state of the objects when passed are kept for the length of their life cycle. As an example, if a numeric value is kept in a variable, each mocked call will return that numeric value regardless if its changed before calling a subsequent mocked call.
You must also disable Optimizations for the project in Visual Studio or else it may not make the mocking calls as it will remove macro mocking code.
To do this, right click on project then click on properties. Expand Configuration Properties, then expand C/C++ and click on Optimization. At the top, be sure it says for Optimization "Disabled (/Od)".
To start using Hippomocks, you first will need to create the repository. This contains a list of calls you wish to mock, as well as the criteria of what to do, when to do it, and in which order to do it. It also keeps track of the objects that should or should not have been mocked. When debugging, don't be afraid to look at the structure of this object, it has a lot of great information in it.
To create the repository, you simply create an instance of it like this:
MockRepository mocks;
From here, you can determine if you need to do class based calls, or static function calling which is explained below.
In all cases, you can specify some of the things you want it to do, and in what scenarios you want it mock. For the sake of ease, we'll use the static methods first. Here is an example of how you might mock a simple call.
MockRepository mocks;
mocks.OnCallFunc(Sleep);
MyFunction();
After you have your repository set up, you will tell it that always you want the "Sleep" function to simply do nothing, and just return. Now that you have your mock set up, you will call your function "MyFunction", and if it makes a call to Sleep(), it will just return immediately instead of actually calling the Sleep() call.
In this case the return was a void, so there was no need to return anything, but if we need to return a value, then we can specify it with the "Return" like so:
MockRepository mocks;
mocks.OnCallFunc(ftell).Return(10);
MyFunction();
In this case, we are telling it that if the ftell is ever called, we want it to return the value of 10 instead of trying to call ftell.
(Under construction)
For those that just want to jump in and experiment to get a feel of the mocking system, here is a quick way to try a few things out.
MockRepository mocks;
bool voFunc();
mocks.ExpectCallFunc(voFunc).Return(true); // Returns bool, takes no values,
// Expected to be called first time, returns true.
mocks.ExpectCallFunc(voFunc).Return(false); // Same thing, but is expected to be called a
// second time and will return false.
mocks.OnCallFunc(voFunc).Return(true); // from now on, it will return true.
void voFuncTwo(int nVal);
mocks.OnCallFunc(voFuncTwo).With(5); // Has no return, but we only want to intercept it when 5
// is passed in, and whenever it is called will act this way.
int voFuncThree(string nVal);
mocks.OnCallFunc(voFuncThree).Return(1); // Always return 1 regardless of the string
mocks.OnCallFunc(voFuncThree).With("three").Return(3); // return the number 3 if the text "three"
// is passed in.
- Under construction - Please refer to the old code/documentation links below) Most of the time we deal with classes on our code and to better isolate some function calls we mock the class to control what a class function does or the function(class or API) inside it behaves.
Some things to note about when mocking classes:
-
Mocking a class does not call the constructor.
-
Member variables are not initialized after mocking. So any calls to them might result to erroneous behavior or even crash. So better initialize them if you can.
-
Mocking class functions require the function to be virtual.
-
Sometimes you don't need to mock a class most of the time. Think about it first if you really need class mocking since you can live with instantiating the class and mocking the APIs inside the function. Class based calls that can be made are:
-
OnCall
-
OnCalls
-
ExpectCall
-
ExpectCalls
-
NeverCall
-
OnCallOverload
-
ExpectCallOverload
-
NeverCallOverload
-
OnCallDestructor
-
ExpectCallDestructor
-
NeverCallDestructor
Mocking a Class:
MockRepository mocks;
MiniDumper* pDump = mocks.Mock<MiniDumper>(); //this will create a mocked MiniDumper object
Mocking a function within a class:
MockRepository mocks;
Logger* pLogger = mocks.Mock<Logger>();
//Important that Flush needs to be virtual or else you'll get an "Function called without expectation!" error
mocks.OnCall(pLogger, Logger::Flush);
pLogger->Flush(); //calling this won't call the real Flush function
Sample usage of class mocking
//Let's say we have two class functions under CFoo, Func1 and Func2
BOOL CFoo::Func1()
{
BOOL bRet = FALSE;
if(Func2()) {
//do something
bRet = TRUE;
}
else {
//do something else
}
return bRet;
}
BOOL CFoo::Func2()
{
//something that returns TRUE/FALSE, it could be connecting to some server or downloading a 10gig file
}
void CFoo_ut
{
MockRepository mocks;
CFoo* pFoo = mocks.Mock<CFoo>();
mocks.ExpectCall(pFoo, CFoo::Func2).Return(TRUE);
CPPUNIT_ASSERT(pFoo->Func1() == TRUE); //call to Func1 will result to a mocked Func2 call that returns TRUE
mocks.ExpectCall(pFoo, CFoo::Func2).Return(FALSE);
CPPUNIT_ASSERT(pFoo->Func1() == FALSE); //call to Func1 will result to a mocked Func2 call that returns FALSE
}
//this way we can control the behavior of Func2 without performing the heavy task it could do when not mocking it.
Mocking overloaded class functions:
//let's say you have an overloaded class function
class Foo
{
public:
..
virtual BOOL Func(std::string param1);
virtual BOOL Func(int param1);
};
MockRepository mocks;
Foo* pFoo = mocks.Mock<Foo>();
mocks.ExpectCallOverload(pFoo, (BOOL(Foo::*)(std::string param1))&Foo::Func).Return(TRUE);
When mocking overloaded class functions you have to specify which overloaded function will be called unlike on normal functions that HippoMocks will know which function is being called.
Below is an example where I don't need to mock the class:
Logger tempLogger;
MockRepository mocks;
wstring wsLogFile = GetTestFilesDirectory() + L"TempLog.txt";
RemoveFile(wsLogFile);
//test where m_hLogFile is NULL
CPPUNIT_ASSERT(tempLogger.Truncate() == false);
tempLogger.SetLogFile(wsLogFile);
mocks.OnCallFunc(fflush).Return(0);
mocks.OnCallFunc(fclose).Return(0);
mocks.OnCallFunc(InterlockedIncrement).Return(0);
mocks.OnCallFunc(InterlockedDecrement).Return(0);
mocks.OnCallFunc(CloseHandle).Return(TRUE);
mocks.OnCallFunc(SetFilePointerEx).Return(TRUE);
mocks.OnCallFunc(GetSystemTime);
mocks.OnCallFunc(SystemTimeToFileTime).Return(TRUE);
mocks.OnCallFunc(SetFileTime).Return(TRUE);
mocks.OnCallFunc(GetTickCount).Return(0);
mocks.ExpectCallFunc(WP_CreateFile).Return(INVALID_HANDLE_VALUE);
//WP_CreateFile should return INVALID_HANDLE_VALUE
CPPUNIT_ASSERT(tempLogger.Truncate() == false);
//SetEndOfFile fails
mocks.OnCallFunc(WP_CreateFile).Return((HANDLE)0x1);
mocks.ExpectCallFunc(SetEndOfFile).Return(FALSE);
CPPUNIT_ASSERT(tempLogger.Truncate() == false);
mocks.ExpectCallFunc(SetEndOfFile).Return(TRUE);
CPPUNIT_ASSERT(tempLogger.Truncate() == true);
You can see a lot of OnCallFunc
calls, reason for that is those functions won't change its behavior on the next tests. ExpectCallFunc
calls on the other hand will ensure that when the function we are testing is called that it will behave like what we wanted. Class mocking is not needed in this case since Logger::Truncate
does not have any class function calls that I might want to test by changing its behavior. If there is, it could be not essential to the function we are testing since it might not really affect the end result.
If you want to filter things by certain parameters, you can also supply an underscore which means "don't care" like such:
int voFuncMulti(int magicval, string val, int value);
mocks.OnCallFunc(voFuncMulti).With(2, _, 10).Return(20);
// if someone puts a 2 in the magicval and a 10 in the count, and we dont care
// what the string is, then have it spit out a 20
One of the most powerful features of Hippomocks is the ability to mock non-virtual static calls. As an example, you may have a function that requires you to perform file operations, however when unit testing, you should not be doing costly things like that.
Take our example function here:
#include <stdio.h>
long getfilesize(char* filename)
{
FILE * pFile;
long filesize = -1;
pFile = fopen (filename , "r");
if (pFile != NULL)
{
filesize = ftell(pFile);
fclose (pFile);
}
return filesize;
}
Now what we want to test here is the getfilesize, but we don't want to be opening files and such, so we mock the file IO calls.
Say we just want to test it where it always returns a file size of 100. We would mock the call and tell it to return 100 like this:
MockRepository mocks;
mocks.OnCallFunc(ftell).Return(100);
However if we want to prevent it from doing any file IO, but still return a desired result, we would mock it like this:
MockRepository mocks;
mocks.OnCallFunc(fopen).Return((FILE*) 1);
mocks.OnCallFunc(ftell).Return(100);
mocks.OnCallFunc(fclose).Return(0);
In this case, we actually never open or close the file, but still fulfill the requirement of not returning a NULL.
There are scenarios that the return value is not sufficient while testing or sometimes the function is returning values through "pass by reference". Luckily HippoMocks has a way to control how those functions behave by using the Do() method. Basically it allows us to mock the function and control how it behaves internally by running a different code inside which of course we implemented. Below are the different snippets that will explain the essential parts of how to use the method.
Logger* tempLogger = new Logger;
MockRepository mocks;
MockHelper::GetFileAttributesExW mhGFA;
..
mhGFA.initPtr();
tempLogger = new Logger;
mhGFA.m_pData->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; //set the file attribute that it will return to FILE_ATTRIBUTE_DIRECTORY
mocks.ExpectCallFunc(GetFileAttributesEx).Do(mhGFA).Return(TRUE); //here is the Do method
mocks.ExpectCallFunc(GetFileAttributes).Return(FILE_ATTRIBUTE_READONLY);
mocks.OnCallFunc(SetFileAttributes).Return(TRUE);
tempLogger->EnsureOpen();
delete tempLogger; tempLogger = NULL;
mhGFA.deinitPtr();
//we are testing this code in Logger::CkecIsUTF8
WIN32_FILE_ATTRIBUTE_DATA wData;
//we are mocking this call and we want wData to return a file attribute with FILE_ATTRIBUTE_DIRECTORY
if( !GetFileAttributesEx(m_wsLogFile.c_str (), GetFileExInfoStandard, &wData) ) {
return;
}
if (!(wData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { //we wanted this call to fail
..
..
Ok, so we know what we want to test and how to use it but it still does not explain what and how it happened. Below is the snippet that modify the behavior of the mocked function.
..
class GetFileAttributesEx
{
public:
GetFileAttributesEx()
{
reset();
}
BOOL m_bReturn;
// This is the class' copy where any modifications we do goes;
// remember "dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;" above
WIN32_FILE_ATTRIBUTE_DATA* m_pData;
void reset()
{
m_bReturn = FALSE;
m_pData = NULL;
}
void initPtr()
{
m_pData = new WIN32_FILE_ATTRIBUTE_DATA;
memset(m_pData, 0, sizeof(WIN32_FILE_ATTRIBUTE_DATA));
}
void deinitPtr()
{
delete m_pData;
m_pData = NULL;
}
BOOL operator()(LPCWSTR /*lpFileName*/, GET_FILEEX_INFO_LEVELS /*fInfoLevelId*/, LPVOID lpFileInformation)
{
if (lpFileInformation != NULL && m_pData != NULL)
memcpy_s(lpFileInformation, sizeof(WIN32_FILE_ATTRIBUTE_DATA), m_pData, sizeof(WIN32_FILE_ATTRIBUTE_DATA)); //copies everything
return m_bReturn;
}
};
..
Basically it is a class named exactly the same as the one we are mocking with member variables for return and other we want to control. The important part here is the operator, it has the same parameters as the mocked function and whenever the function is called the operator is called. This is how we can control what the function does internally and return what we want for testing.