I'm trying to mock a simple function from the Linux standard library. strerror()
returns the error message from an errno. This is my library with the function to mock:
~$ cat mylib.c
#include <string.h>
#include <stdio.h>
int myStrerror()
{
int error_number = 0;
char* buffer = strerror(error_number);
fprintf(stdout, "Returned string = '%s'\n", buffer);
return 0;
}
#if defined (EXECUTABLE)
int main(int argc, char **argv)
{
return myStrerror();
}
#endif
~$ g++ -pedantic-errors -Wall -c mylib.c
This is my google test:
~$ cat test_mylib.cpp
#include "gtest/gtest.h"
#include "gmock/gmock.h"
int myStrerror();
class strerrorMock {
public:
MOCK_METHOD(char*, strerror, (int));
};
strerrorMock strerrorMockObj;
char *strerror(int error_number) {
return strerrorMockObj.strerror(error_number);
}
TEST(MockTestSuite, strerror)
{
using ::testing::Return;
char response[] = "mocked strerror function";
EXPECT_CALL(strerrorMockObj, strerror(0))
.WillOnce(Return(response));
EXPECT_EQ(myStrerror(), 0);
::testing::Mock::VerifyAndClearExpectations(&strerrorMockObj);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
~$ g++ -pedantic-errors -Wall \
-o test_mylib.a \
-I"$BUILD_DIR"/googletest-src/googletest/include \
-I"$BUILD_DIR"/googletest-src/googlemock/include \
test_mylib.cpp \
"$BUILD_DIR"/lib/libgtestd.a \
"$BUILD_DIR"/lib/libgmockd.a \
./mylib.o \
-lpthread
This is what it returns normally:
~$ ./mylib.a
Returned string = 'Success'
and Running the test gives this:
~$ ./test_mylib.a
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from MockTestSuite
[ RUN ] MockTestSuite.strerror
Returned string = 'mocked strerror function'
[ OK ] MockTestSuite.strerror (0 ms)
[----------] 1 test from MockTestSuite (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[ PASSED ] 1 test.
test_mylib.cpp:32: ERROR: this mock object (used in test MockTestSuite.strerror) should be deleted but never is. Its address is @0x56114aa239e0.
ERROR: 1 leaked mock object found at program exit. Expectations on a mock object are verified when the object is destructed. Leaking a mock means that its expectations aren't verified, which is usually a test bug. If you really intend to leak a mock, you can suppress this error using testing::Mock::AllowLeak(mock_object), or you may use a fake or stub instead of a mock.
What have I to do to avoid the memory leak?
After some month using the solution of my first answer on several platforms I found that it is not very stable. In particular on MS Windows I had trouble that GoogleMock does not always find the mocking function. So I decided to accept minimal modification of the production code and use a wrapper class for the free system functions as recommended by googletest.
With the following I only have to add a header file to the production code and change its system calls for example
# from
fd = fopen("openclose.txt", "a");
# to
fd = stdioif->fopen("openclose.txt", "a");
On Microsoft Windows I have cloned googletest from github built it using powershell with settings cmake -S . -B build
then cmake --build build --config MinSizeRel
and stay in its root directory using this structure:
├── build
│ └── lib
│ └── MinSizeRel
│ ├── gmock.lib
│ ├── gmock_main.lib
│ ├── gtest.lib
│ └── gtest_main.lib
├── include
│ └── stdioif.h
├── src
│ ├── main.cpp
│ ├── openclose.cpp
│ └── test_openclose.cpp
├── main.exe
├── main.obj
├── openclose.txt
├── test_openclose.exe
└── test_openclose.obj
Here is the header file:
#ifndef INCLUDE_STDIOIF_H
#define INCLUDE_STDIOIF_H
#include <stdio.h>
class Istdio {
// Interface to stdio system calls
public:
virtual ~Istdio() {}
virtual FILE* fopen(const char* pathname, const char* mode) = 0;
virtual int fprintf(FILE* stream, const char* format) = 0;
virtual int fclose(FILE* stream) = 0;
};
// Global pointer to the current object (real or mocked), will be set by the
// constructor of the respective object.
Istdio* stdioif;
class Cstdio : public Istdio {
// Real class to call the system functions.
public:
virtual ~Cstdio() {}
// With the constructor initialize the pointer to the interface that may be
// overwritten to point to a mock object instead.
Cstdio() { stdioif = this; }
FILE* fopen(const char* pathname, const char* mode) override {
return ::fopen(pathname, mode);
}
int fprintf(FILE* stream, const char* format) override {
return ::fprintf(stream, format);
}
int fclose(FILE* stream) override {
}
};
// This is the instance to call the system functions. This object is called
// with its pointer stdioif (see above) that is initialzed with the
// constructor. That pointer can be overwritten to point to a mock object
// instead.
Cstdio stdioObj;
/*
* In the production code you must call it with, e.g.:
stdioif->fopen(...)
* The following class should be coppied to the test source. It is not a good
* idea to move it here to the header. It uses googletest macros and you always
* hove to compile the code with googletest even for production and not used.
class Mock_stdio : public Istdio {
// Class to mock the free system functions.
public:
virtual ~Mock_stdio() {}
Mock_stdio() { stdioif = this; }
MOCK_METHOD(FILE*, fopen, (const char* pathname, const char* mode), (override));
MOCK_METHOD(int, fprintf, (FILE* stream, const char* format), (override));
MOCK_METHOD(int, fclose, (FILE* stream), (override));
};
* In a gtest you will instantiate the Mock class, prefered as protected member
* variable for the whole testsuite:
Mock_stdio mocked_stdio;
* and call it with: mocked_stdio.fopen(...) (prefered)
* or stdioif->fopen(...)
*/
#endif // INCLUDE_STDIOIF_H
This is the simple example program:
#include "stdioif.h"
#include <iostream>
int openclose() {
FILE* fd = nullptr;
int rc = 0;
fd = stdioif->fopen("openclose.txt", "a");
if(fd == NULL) {
std::cerr << "Error opening file\n";
return 1;
}
rc = stdioif->fprintf(fd, "hello world :-)\n");
if(rc < 0) {
std::cerr << "Error appending to file with return code: " << rc << "\n";
stdioif->fclose(fd);
return rc;
}
rc = stdioif->fclose(fd);
if(rc) {
std::cerr << "Error closing file with return code: " << rc << "\n";
return rc;
}
std::cout << "done.\n";
return 0;
}
I execute it with:
#include "src/openclose.cpp"
int main() {
return openclose();
The test program looks like this:
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "stdioif.h"
#include "src/openclose.cpp"
using ::testing::_;
using ::testing::Return;
class Mock_stdio : public Istdio {
// Class to mock the free system functions.
public:
virtual ~Mock_stdio() {}
Mock_stdio() { stdioif = this; }
MOCK_METHOD(FILE*, fopen, (const char* pathname, const char* mode), (override));
MOCK_METHOD(int, fprintf, (FILE* stream, const char* format), (override));
MOCK_METHOD(int, fclose, (FILE* stream), (override));
};
class OpenCloseTestSuite: public ::testing::Test {
protected:
// Member variables of the whole testsuite: instantiate the mock objects.
Mock_stdio mocked_stdio;
};
TEST_F(OpenCloseTestSuite, open_close) {
EXPECT_CALL(mocked_stdio, fopen(_, _))
.WillOnce(Return((FILE*)0x123456abcdef));
EXPECT_CALL(mocked_stdio, fprintf(_,_))
.WillOnce(Return(-1));
EXPECT_CALL(mocked_stdio, fclose(_)).Times(1);
// process unit
EXPECT_EQ(openclose(), 0);
}
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
To compile it on Microsoft Windows I use:
cl -nologo /EHsc -I. -I.\include -I.\googletest\include -I.\googlemock\include .\build\lib\MinSizeRel\gtest.lib .\build\lib\MinSizeRel\gmock.lib .\src\[main.cpp | test_openclose.cpp]