Mocking Free Function

Mocking free function

No it's not possible, without changing the sources, or bringing your own version of foo() that is linked with the executable code.


From GoogleMock's FAQ it says

My code calls a static/global function. Can I mock it?

You can, but you need to make some changes.

In general, if you find yourself needing to mock a static function, it's a sign that your modules are too tightly coupled (and less flexible, less reusable, less testable, etc). You are probably better off defining a small interface and call the function through that interface, which then can be easily mocked. It's a bit of work initially, but usually pays for itself quickly.

This Google Testing Blog post says it excellently. Check it out.

Also from the Cookbook

Mocking Free Functions

It's possible to use Google Mock to mock a free function (i.e. a C-style function or a static method). You just need to rewrite your code to use an interface (abstract class).

Instead of calling a free function (say, OpenFile) directly, introduce an interface for it and have a concrete subclass that calls the free function:

class FileInterface {
 public:
...
virtual bool Open(const char* path, const char* mode) = 0;
};
class File : public FileInterface {
 public:
...
virtual bool Open(const char* path, const char* mode) {
return OpenFile(path, mode);
}
};

Your code should talk to FileInterface to open a file. Now it's easy to mock out the function.

This may seem much hassle, but in practice you often have multiple related functions that you can put in the same interface, so the per-function syntactic overhead will be much lower.

If you are concerned about the performance overhead incurred by virtual functions, and profiling confirms your concern, you can combine this with the recipe for mocking non-virtual methods.


As you mentioned in your comment that you actually provide your own version of foo(), you can easily solve this having a global instance of another mock class:

struct IFoo {
virtual A* foo() = 0;
virtual ~IFoo() {}
};

struct FooMock : public IFoo {
FooMock() {}
virtual ~FooMock() {}
MOCK_METHOD0(foo, A*());
};

FooMock fooMock;

// Your foo() implementation
A* foo() {
return fooMock.foo();
}

TEST(...) {
EXPECT_CALL(fooMock,foo())
.Times(1)
.WillOnceReturn(new MockA());
// ...
}

Don't forget to clear all call expectations, after each test case run.

Google Mock a free system function on Linux always finishes with 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]

How to mock a free function in python?

This is a perfectly fine way to do it. As long as you know that BlissLiesHere does not change the overall behavior of the unit you are testing...

EDIT:

This is what is being done, under all the nice extras they provide, by different kinds of mocking libraries, such as Mock, Mox, etc.

How to mock system call in C++ Unit Testing during runtime using GoogleMock?

You can refer to mocking-free-functions in Gmock's documentation.

Unfortunately, using the recommended method means you will have to change your code, which may not work if your code need to strictly be a C code.

However, if you are willing to accept this, based on the documentation, you will have to create a wrapper around all the system functions that you are using and then mock that wrapper.

Also, don't forget that you will have to add EXPECT_CALL or ON_CALL for all functions that are expected to be called and return a non-default (i.e. 0) value. For example, in your first test, you should also provide EXPECT_CALL or ON_CALL for fopen.

Here is an example implementation:

// System wrapper interface class.
class MySystemWrapperInterface {
public:
virtual FILE* fopen(const char* filename, const char* mode) = 0;
virtual int fseek(FILE* stream, long int offset, int whence) = 0;
virtual size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream) = 0;
virtual int ferror(FILE* stream) = 0;
virtual int fclose(FILE* stream) = 0;
};

// System wrapper actual class used in production code.
class MySystemWrapperClass : public MySystemWrapperInterface {
public:
FILE* fopen(const char* filename, const char* mode) {
return ::fopen(filename, mode);
}
int fseek(FILE* stream, long int offset, int whence) {
return ::fseek(stream, offset, whence);
}
size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream) {
return ::fread(ptr, size, nmemb, stream);
}
int ferror(FILE* stream) { return ::ferror(stream); }
int fclose(FILE* stream) { return ::fclose(stream); }
};

// Mocked System wrapper used for testint.
class MySystemWrapperMockClass : public MySystemWrapperInterface {
public:
MOCK_METHOD(FILE*, fopen, (const char*, const char*), (override));
MOCK_METHOD(int, fseek, (FILE*, long int, int), (override));
MOCK_METHOD(size_t, fread, (void*, size_t, size_t, FILE*), (override));
MOCK_METHOD(int, ferror, (FILE*), (override));
MOCK_METHOD(int, fclose, (FILE*), (override));
};

// Wrapper class for your own readdata function.
class MyClass {
// The system wrapper passed by dependency injection through constructor.
MySystemWrapperInterface* system_wrapper_;

public:
// Initialize the system wrapper in constructor.
MyClass(MySystemWrapperInterface* system_wrapper)
: system_wrapper_(system_wrapper) {}

int readdata(void* data, int size, int offset, char* filename) {
if (data == NULL || size == 0 || filename == NULL) {
return -1;
}

FILE* fp = system_wrapper_->fopen(filename, "rb");
if (fp == NULL) {
return -1;
}

if (system_wrapper_->fseek(fp, offset, SEEK_SET) != 0) {
system_wrapper_->fclose(fp);
return -1;
}

system_wrapper_->fread(data, size, 1, fp);

if (system_wrapper_->ferror(fp)) {
system_wrapper_->fclose(fp);
return -1;
}
system_wrapper_->fclose(fp);
return 1;
}
};

TEST(test, Test1fseek) {
// Create the mock object and inject it into your class.
MySystemWrapperMockClass mock_system_wrapper;
MyClass my_object(&mock_system_wrapper);

// When fseek called in readdata API call mock fseek to hit fseek fail
// fseek(){return -1;}

// IMPORTANT: Don't forget to add EXPECT_CALL or ON_CALL for all functions
// that are expected to be called.
EXPECT_CALL(mock_system_wrapper, fopen)
.Times(1)
.WillOnce(Return(reinterpret_cast<FILE*>(0x1)));

EXPECT_CALL(mock_system_wrapper, fseek).Times(1).WillOnce(Return(1));
EXPECT_CALL(mock_system_wrapper, fclose).Times(1).WillOnce(Return(1));

char data[10] = {0};
int status = my_object.readdata(data, sizeof(data), 0, "test.txt");
EXPECT_EQ(status, -1);
}

TEST(test, Test2fread) {
// Create the mock object and inject it into your class.
MySystemWrapperMockClass mock_system_wrapper;
MyClass my_object(&mock_system_wrapper);

// When fread called in readdata API call mock fread to hit read fail
// fread(){return -1;}
// IMPORTANT: Don't forget to add EXPECT_CALL or ON_CALL for all functions
// that are expected to be called.
EXPECT_CALL(mock_system_wrapper, fopen)
.Times(1)
.WillOnce(Return(reinterpret_cast<FILE*>(0x1)));
EXPECT_CALL(mock_system_wrapper, fseek).Times(1).WillOnce(Return(0));
EXPECT_CALL(mock_system_wrapper, fread).Times(1).WillOnce(Return(-1));
EXPECT_CALL(mock_system_wrapper, ferror).Times(1).WillOnce(Return(-1));
EXPECT_CALL(mock_system_wrapper, fclose).Times(1).WillOnce(Return(1));

char data[10] = {0};
int status = my_object.readdata(data, sizeof(data), 0, "test.txt");
EXPECT_EQ(status, -1);
}

Live example: https://godbolt.org/z/qxf74fWGh



Related Topics



Leave a reply



Submit