Reputation: 1475
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?
Upvotes: 12
Views: 5587
Reputation: 26476
you can mimic simple HTTP server with a socket. the following psuedo code may help:
1) set up a string to send from the client, take its length in before hand
2) open a new thread with a socket in it
3) bind the socket into some port , listen and accept new connection
4) send the string you have setted with your http client
5) in the socket side, read until the length you saved has reached, save that string for comparison
6) send some pre-defined http response
7) close the socket
7) close the thread
8) continue testing, you have the string which the server got, and the string which the client got, and the original strings which these was originated from
mimicing chunked transfer and redirections is easy, but mimicing SSL can be quite difficult. you can wrap your socket with some SSL stream provided with openSSL or Boost SSL stream.
another option is to use already-written HTTP server on the local host. writing one (for testing only) in Python or Node.js is pretty easy and suitable for that task of testing. before the test is launched, activate the server script (with Node.js script, it is easy as system("node myServer.js")
) and when the test is done, kill that server.
Upvotes: 0
Reputation: 25581
Inject an abstract client session instance in the constructor. Mock it in unit tests and pass a real instance when running for real.
You say you can't mock it in one sentence and in the next sentence you say you can - what do you mean? If you mean that the session class isn't "yours" or that it can't be derived from to be mocked that way, then did you try wrapping it in a class of yours, so that it can be mocked?
Also, you say "I think that the way of implementing of session etc. should by hidden in my class."
Your fallacy in that assumption is that your class, I guess you mean HTTPClient
, has nothing to do with it - it's the session class that should hide its own implementation, and that it can do also if you pass it as an instance in the constructor, which also adds infinite flexibility overall.
Upvotes: 1
Reputation: 118292
I happned to have written an HTTP client library the other day.
To test the HTTP client library, I just wrote simple test code that started a std::thread
listening on some random port on localhost
. Then I told the client to make a test request with the host
and the port
parameters, as in your case, pointing to the port that my thread was now listening on. The thread's code was programmed to accept a connection, read an HTTP request, save it, and then respond with a canned HTTP response.
And that's how I tested my client library, verifying both the actual request the client sent, and how the client handled the canned HTTP response. Later on, I developed this unit test code to send various kinds of HTTP errors, and malformed HTTP responses, in order to test and verify how the client code handled those situations.
And, for a good measure, the whole thing was guarded by an alarm()
call, so if something got stuck in an infinite loop, or so, the entire process will eventually commit suicide.
And that's how you can test your own code too, the same way.
Upvotes: 10