0

I have existing C++ code, which creates a child process using fork() system call. And child process executes linux command using execlp() system call. Now I want to test this code using gmock framework with 100% code coverage. I googled a lot but I did not get any full proof solution. Can anybody help me with this?

This is my SUT:

int someclass :: os_fork()
{
  pid_t pid = fork();
  if (pid == -1) {
  cout<<"fork() failed with errno" <<errno <<endl;
  return false;
  }

  if (pid == 0 && (execlp("ls", "ls", nullptr) != 0))
  {
  cout<<"child process failed with errno"<<errno<<endl;
  return false;
  }
  int ppid = pid;
  return 0;
}

I want to mock fork() and execlp() system calls. How could I do that?

273K
  • 29,503
  • 10
  • 41
  • 64

2 Answers2

3

It's not possible to mock global functions as they are. Instead, you can add a layer of abstraction and wrap system calls in an interface:

class OsInterface {
    virtual pid_t fork() = 0;
}

class OsInterfaceImpl : public OsInterface {
    virtual pid_t fork() override 
    {
        return ::fork();
    }
}

class OsInterfaceMock : public OsInterface {
    MOCK_METHOD0(fork, pid_t());
}

This allows you to choose the real system call or mock with Dependency Injection. Since you didn't provide any code, I cannot help you with that.

Typically you pass a pointer or a reference to the injected class to the constructor of class that should use it, but there are few other methods that could suit your project better.

Additional advantage is that this code is more open-closed: You can easily (well, relatively easily) add for example Windows implementation without changing existing code - you'd simply provide another class inheriting from OsInterface. You don't have to change any call to interface in your production code, just change the injected class.

Note that this will not run any additional processes in unit tests, but this is an advantage. You should test the code run in process separately.

Yksisarvinen
  • 18,008
  • 2
  • 24
  • 52
  • Thanks @Yksisarvinen for response. I tried this approach but its not working. Could you please explain how to use this mocked object in actual test case? – Adarsh Patel Jan 14 '19 at 06:48
0

It's a bit late, but you can just redefine it, and "inject" MockFunction to make some expectations. Some kind of hooking i guess.

#define CALL_ORIGINAL -2    
testing::MockFunction<int()> forkHelp;

pid_t fork(void)
{
    auto mockResult = forkHelp.Call();
    if (CALL_ORIGINAL != mockResult)
    {
        return mockResult;
    }
    return syscall(SYS_fork); /* Hack for calling real syscall */
}

P.S. This approach has its downfall. At the end of the test it will say your code leaaks), so it's up to you to workaround this.