3

We are now introducing unit tests in our company and thinking about best way of mocking system calls.

Consider the following code

    int fd = open(path, O_RDONLY);
    if (fd < 0) {
        LOG.error() << "cannot load plugin " << path << std::endl;
        return ERROR(ERROR_OPENING_PLUGING);
    }
    // do other stuff

Obviously, we need to mock system call to open

I have found following ways to do so:

  1. Correct - in the terms of design but ugly way. Create interface and impl

    class ISystem
    {
    public:
        typedef std::auto_ptr<ISystem> Ptr;
    
        ISystem() {};
        virtual ~ISystem() {};
    
        virtual int open(const char* file, int path) = 0;
    };
    
    class System : public ISystem
    {
    public:
        System() {};
        virtual ~System() {};
    
        virtual int open(const char* file, int path);
    
        static ISystem::Ptr Get();
    };
    

and use it

    Common::Error dlopen_signed(ISystem::Ptr& system, const char* path, int flags, void*& ret)
    {
    int fd = system->open(path, O_RDONLY);
    if (fd < 0) {
        LOG.error() << "cannot load plugin " << path << std::endl;
        return ERROR(ERROR_OPENING_PLUGING);
    }
    char fd_path[32];

I dont like it because every function will need to have one more argument - ISystem::Ptr& system, that will be the same for all production code.

Also, not sure about speed (this involves extra layers for basic system calls that must be really fast)

2) Use a link seam Linker is designed so that it prefer your versions of functions than system ones.

But this does not work for some system calls, for example open (not sure about the reason), and this solution is a little bit hackish.

3) use --wrap compiler functionality

--wrap symbol Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to __wrap_symbol. Any undefined reference to __real_symbol will be resolved to symbol. This can be used to provide a wrapper for a system function. The wrapper function should be called __wrap_symbol. If it wishes to call the system function, it should call __real_symbol. Here is a trivial example:

void *
__wrap_malloc (int c)
{
  printf ("malloc called with %ld\n", c);
  return __real_malloc (c);
} 

This solution is nice but does not work for all compilers I guess.

The question is - which one are you using on your projects?

Spook
  • 25,318
  • 18
  • 90
  • 167
Georgy Buranov
  • 1,296
  • 1
  • 16
  • 26

3 Answers3

2

You have to draw a line to what to mock, and what to unit test. 100% unit tests coverage is not the ultimate goal.

If you really want to mock systam calls, the best is to put them in a wrapper (the first option in the question). Not one huge wrapper, but you should split them into several wrappers by functionality.

BЈовић
  • 62,405
  • 41
  • 173
  • 273
0

System calls are infrastructure
We wrap infra logic using a wrapper
Also design the wrapper such that there can be many wrappers based on context.
Also I agree with "BЈовић" 100% coverage is not the ultimate goal - balance it.

Venu b
  • 428
  • 1
  • 3
  • 9
-1

(1) example is incorrect about mock, maybe because you don't want to clarify what you are doing and your example doesn't show me what mocks what. Mocking is actually a practical exemplified use of polymorphism. People tend to wrap all things up in wrapper functions and then even in wrapper classes

IParam param=new ParamMock(xxx);
File file(param); // mocked
....
file.open(xxx)

(2) I don't know about it

(3) definitely it shows you know too much and spend too much effort for unit-testing. I always put in mind that unit-tests will be thrown away after all. What development process are you following ?

  • we are switching to agile – Georgy Buranov Nov 05 '13 at 10:29
  • Your question tells me hints that you are working with waterfall model or doing some upgrade tasks for already made software packages, etc. But please don't mock the legacy code... code coverage tends to be around 70%-85%, we don't need much more for unit-testing purposes. There are still bugs or defects reported after your first deliverable package and less in the next on for you to fix. – Lovely Pufferfish Nov 05 '13 at 10:44
  • 1
    Your answer is heavily opinion based and doesn't answer the original question, even remotely. – Zak Dec 14 '13 at 22:30