4

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?

asked Apr 7, 2016 at 1:43
1
  • If you want to badly enough, you can mock. Have your HTTP client use (for example) a Socket class, and by providing a mock Socket class, you can look at what gets sent, send mock replies, etc. Commented Apr 7, 2016 at 1:56

2 Answers 2

4

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"?

answered Apr 7, 2016 at 2:12
0

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?

Yes. You should definitely inject the connection/session into the client.

I think that the way of implementing of session etc. should by hidden in my class.

Maybe; If it should, split HttpClient into three:

  • a client implementation class, as a template over the session type:

    template <typename SessionT>
    class HTTPClientImpl { /* injected session, see @5gon12eder's answer */... };
    
  • a session implementation;

  • an extremely thin client putting it all together:

    class HttpClient: private HTTPClientImpl<SessionT> // session type is now
     // an implementation detail,
     // invisible outside of HttpClient
    {
    };
    

    This class would not be tested; Instead, test the implementation class.

Regarding the testablility of your class, you should test it with a real network connection and a mock server (that serves hard-coded content). Here is a sample HTTP server implementation; It should take less than a workday to take the code, integrate it into your build and change it into an active object (that is running a server in the background) that returns predefined responses. such an active object should allow you to write multiple unit tests with various server responses, quickly.

By doing this, you will get lots of errors that are difficult to mock or simulate without network traffic (for example, the non-deterministic breadown of larger messages into data chunks, depending on current network traffic).

answered Apr 8, 2016 at 8:51

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.