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

How to write unit test for network client?

本文关键字:单元测试 客户端 网络 何编写      更新时间:2023-10-16

我需要编写一个简单的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对象(在测试中我会通过模拟),但这是一个好的设计吗?我认为会话等的实现方式应该隐藏在我的课堂中。

你知道吗?

前几天我碰巧写了一个HTTP客户端库。

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

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

而且,在很大程度上,整个过程都是由alarm()调用保护的,所以如果某个东西陷入了无限循环,那么整个过程最终都会自杀。

这就是你如何测试你自己的代码,同样的方式。

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

你说你不能在一句话中嘲笑它,下一句话你说你可以——你是什么意思?如果你的意思是会话类不是"你的",或者它不能以这种方式被嘲笑,那么你有没有试着把它包装在你的类中,这样它就可以被嘲笑?

此外,你说"我认为会话等的实现方式应该隐藏在我的课堂上。"

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

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

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")一样简单),测试完成后,终止该服务器。