Шаблоны для модульного тестирования метода С++, который вызывает стандартный вызов библиотеки

Я пишу класс С++ для обертывания сокетов (я знаю, что для этого есть хорошие библиотеки - я катаюсь на практике):

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

Этот класс, в свою очередь, используется несколькими другими, и я знаю, что unit test с googlemock путем подкласса и издевательств.

Но я хотел бы сначала разработать этот класс тест, и я немного застрял в настоящее время. Я не могу использовать googlemock в стандартной библиотеке C (т.е. socket.h, в данном случае), так как класс С++ это не так. Я мог бы создать тонкий класс оболочки С++ вокруг стандартных функций библиотеки C, которые мне нужны, например.

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);
};

Теперь я могу издеваться над этим классом и unit test my Socket (который теперь может быть переименован Network или некоторые из них). LibcWrapper может пригодиться и для других классов, и сам по себе не должен быть модульным, поскольку он просто предоставляет кучу методов класса.

Это начинает звучать хорошо для меня. Отвечаю ли я на мой собственный вопрос или существуют стандартные шаблоны для тестирования, управляющего этим видом развития в С++?

Ответ 1

Я бы, вероятно, издевался над ним, работая через интерфейс сокета (т.е. базовый класс) и реализуя тестовую версию этого базового класса.

Вы можете сделать это несколькими способами, например, проще всего указать весь API сокета в терминах интерфейса С++.

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

Затем укажите конкретную реализацию, которая работала через библиотеку сокетов BSD

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


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

Кодируя по интерфейсу, вы можете создать свою тестовую версию, чтобы делать все, что вам нравится.

Однако мы можем ясно видеть, что этот первый проход довольно странный. Мы обертываем целую библиотеку, это не класс, в том смысле, что он описывает объекты.

Вам лучше подумать о розелях и способах создания сокетов. Это было бы более объектно-ориентированным. Вдоль этих строк я разделил бы функциональность выше на два класса.

   // 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
   };

Для живого использования:

   class CBsdSocketFactory : public ISocketFactory
   {
      ...
   };

   class CBsdSocket : public ISocket
   { 
      ...
   };

Для тестирования:

   class CTestSocketFactory : public ISocketFactory
   {
   };

   class CTestSocket : public ISocket
   {
   };

И отделите вызовы библиотеки BSD на эти два разных класса, которые имеют свои собственные обязанности.

Ответ 3

Google Mock cookbook предлагает нечто подобное в вашем случае.