Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Patterns for unit testing a C++ method that makes a standard library call

I'm writing a C++ class to wrap sockets (I know that there are good libraries for this--I'm rolling my own for practise):

class Socket {
public:
  int init(void); // calls socket(2)
  // other stuff we don't care about for the sake of this code sample
};

This class is in turn used by several others, which I know I can unit test with googlemock by subclassing and mocking.

But I would like to develop this class test first, and am a bit stuck currently. I can't use googlemock on the C standard library (i.e. socket.h, in this case), since a C++ class it is not. I could create a thin C++ wrapper class around the C standard library functions I need, e.g.

class LibcWrapper {
public:
   static int socket(int domain, int type, int protocol);
   static int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
   static int listen(int sockfd, int backlog);
   static int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
   static ssize_t write(int fd, const void *buf, size_t count);
   static int close(int fd);
};

Now I can mock that and unit test my Socket class (which might now need to be renamed Network or some such). LibcWrapper could come in handy for other classes as well, and would not itself need to be unit tested since it just provides a bunch of class methods.

This is starting to sound good to me. Have I answered my own question, or do there exist standard patterns for test driving this sort of development in C++?

like image 463
Josh Glover Avatar asked Apr 25 '11 14:04

Josh Glover


People also ask

How many types of methods we can test by using unit testing?

There are 2 types of Unit Testing: Manual, and Automated.

What can be used for unit testing in C?

The most scalable way to write unit tests in C is using a unit testing framework, such as: CppUTest. Unity. Google Test.


2 Answers

I would probably mock it by working through a socket interface (ie base class) and implementing a test version of that base class.

You can do this a couple of ways, for example the simplest thing is to specify the entire socket API in terms of a C++ interface.

   class ISocket
   {
   public:
        virtual int socket(int domain, int type, int protocol) = 0;
        virtual int bind(int sockfd...) = 0;
        // listen, accept, write, etc            
   };

Then provide a concrete implementation that worked through the BSD sockets library

   class CBsdSocketLib : public ISocket
   {
   public:
       // yadda, same stuff but actually call the BSD socket interface
   };


   class CTestSocketLib : public ISocket
   {
   public:
       // simulate the socket library
   };

By coding against the interface you can create your test version to do whatever you like.

However, we can clearly see that this first pass is rather weird. We're wrapping an entire library, its not really a class in the sense that it describes objects.

You'd rather think in terms of sockets and ways to manufacture sockets. This would be more object oriented. Along those lines I'd separate the functionality above into two classes.

   // responsible for socket creation/construction
   class ISocketFactory
   {
        virtual ISocket* createSocket(...) = 0; // perform socket() and maybe bind()
   };

   // a socket
   class ISocket
   {
         // pure virtual recv, send, listen, close, etc
   };

For live use:

   class CBsdSocketFactory : public ISocketFactory
   {
      ...
   };

   class CBsdSocket : public ISocket
   { 
      ...
   };

For testing:

   class CTestSocketFactory : public ISocketFactory
   {
   };

   class CTestSocket : public ISocket
   {
   };

And separate the BSD library calls into those two distinct classes that have their own responsibilities.

like image 190
Doug T. Avatar answered Sep 27 '22 20:09

Doug T.


I've used that technique as well. Beware that Google Mock doesn't quite support mocking static functions. The FAQ explains that you should use an interface with virtual methods that you can override in the usual Google Mock way.

like image 37
Rob Kennedy Avatar answered Sep 27 '22 20:09

Rob Kennedy