如何为网络客户端编写单元测试?

时间:2016-04-07 01:33:20

标签: c++ unit-testing c++11 googletest gmock

我需要编写简单的http客户端。为我的班级进行单元测试可能会很棒。但我不知道如何写出适当且可测试的课程。

例如,我有一个这样的客户:

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

如何测试send方法(我真的发送了我想要的东西)?我无法嘲笑它。我可以写HTTPClient在构造函数中接收SomeLibrary::ClientSession对象(在测试中我会传递模拟)但是它是好设计吗?我认为会话等的实现方式应该隐藏在我的课堂中。

你有什么想法吗?

3 个答案:

答案 0 :(得分:5)

我很高兴前几天写了一个HTTP客户端库。

为了测试HTTP客户端库,我只编写了一个简单的测试代码,它开始std::thread监听localhost上的一些随机端口。然后我告诉客户端使用hostport参数发出测试请求,就像你的情况一样,指向我的线程正在监听的端口。线程的代码被编程为接受连接,读取HTTP请求,保存它,然后使用预设的HTTP响应进行响应。

这就是我如何测试我的客户端库,验证客户端发送的实际请求以及客户端如何处理预设的HTTP响应。后来,我开发了这个单元测试代码来发送各种HTTP错误和格式错误的HTTP响应,以便测试和验证客户端代码如何处理这些情况。

并且,一个好的衡量标准,整个事情都被alarm()调用所保护,所以如果事情陷入无限循环,那么整个过程最终会自杀。

这就是你可以用同样的方式测试你自己的代码的方法。

答案 1 :(得分:0)

在构造函数中注入抽象客户端会话实例。在单元测试中进行模拟,并在为真实运行时传递一个真实实例。

你说你不能用一句话来嘲笑而在下一句中你说你可以 - 你的意思是什么?如果你的意思是会话类不是“你的”或者它不能从那个方面被嘲笑,那么你是否尝试将它包装在你的类中,以便它可以被嘲笑?

另外,你说“我认为实现会话等的方式应该隐藏在我的课堂中。”

你在这个假设中的谬论是你的类,我猜你的意思是HTTPClient,与它无关 - 它是会隐藏自己的实现的会话类,并且<如果你将它作为构造函数中的实例传递它也可以做到,这也增加了整体的无限灵活性。

答案 2 :(得分:0)

您可以使用套接字模仿简单的HTTP服务器。 以下伪代码可能有所帮助:

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

模仿分块传输和重定向很容易,但模仿SSL可能非常困难。你可以使用openSSL或Boost SSL流提供的一些SSL流包装你的套接字。

另一种选择是在本地主机上使用已写入的HTTP服务器。在Python或Node.js中编写一个(仅用于测试)非常简单,适合于测试任务。在测试启动之前,激活服务器脚本(使用Node.js脚本,它很容易为system("node myServer.js")),并且在测试完成后,终止该服务器。