C++ – How to write unit tests for network client

cc++11gtestunit testing

I need to write simple http client. It could be great to have unit tests for my class. But I don't know how to write proper and testable class.

For example, I have a client like this:

class HTTPClient
{
public:
     HTTPCLient(const std::string& host, const std::string& port): session(host, port) {}

     void send()
     {
         session.sendRequest(someRequest);
         Response response = session.receiveResponse();
         // ...
     }

private:
    SomeLibrary::ClientSession session;
};

How to test send method (that I really send what I want)? I can't mock it. I can write that HTTPClient receives SomeLibrary::ClientSession object in constructor (in test I would pass mock) but is it good design? I think that the way of implementing of session etc. should by hidden in my class.

Do You have any idea?

Best Answer

Yes, it would be a good design to break that direct dependency on the session implementation and instead inject it. (Not only for testing purpose.)

template <typename SessionT>
class HTTPClient
{
private:

  SessionT session_ {};

public:

  HTTPClient(SessionT session) : session_ {std::move(session)}
  {
  }

  // Optional convenience overload
  HTTPClient(const std::string& host, const int port) : session_ {host, port}
  {
  }

  void
  send()
  {
    this->session_.sendRequest(someRequest);
    auto response = this->session_.receiveResponse();
    // …
  }

};

Note the use of auto for the response type. Alternatively, require that a SessionT provides an appropriate nested typedef.

using ResponseT = typename SessionT::response_type;

If you don't want to use templates, you would have to define a virtual interface for your session class and use it through smart pointers. I recommend to stay with the template.

Note that this generalization has purpose beyond unit testing. For example, you might want to try a different library, use HTTPS with different TLS implementations, connect to a local server via UNIX domain sockets, communicate with a server process via a pair of pipes, or even implement client and server in the same process, communicating via a message queue. Not all of these options may sound relevant to you today but it is good to have the flexibility.

By the way, shouldn't your “session” actually be a “connection”?