unit-testing - 如何对修改发送消息的 net.Conn 函数进行单元测试?
问题描述
首先,让我告诉我,我已经看到了与这个类似的其他问题,但我认为没有一个问题能真正详细地回答它(How do one test net.Conn in unit tests in Golang?和How does Golang 单元测试中的一个测试 net.Conn?)。
我要测试的是以某种方式响应 TCP 请求的功能。
在最简单的情况下:
func HandleMessage(c net.Conn) {
s, err := bufio.NewReader(c).ReadString('\n')
s = strings.Trim(s, "\n")
c.Write([]byte(s + " whatever"))
}
我将如何对这样的功能进行单元测试,理想情况下使用net.Pipe()
不打开实际连接。我一直在尝试这样的事情:
func TestHandleMessage(t *testing.T) {
server, client := net.Pipe()
go func(){
server.Write([]byte("test"))
server.Close()
}()
s, err := bufio.NewReader(c).ReadString('\n')
assert.Nil(t, err)
assert.Equal(t, "test whatever", s)
}
但是我似乎无法理解在哪里放置HandleMessage(client)
(或HandleMessage(server)?
在测试中实际获得我想要的响应。我可以让它阻塞并且根本不会完成,或者它将返回我在服务器中编写的完全相同的字符串。
有人可以帮助我并指出我在哪里犯了错误吗?或者在测试 TCP 功能时可能指向正确的方向。
解决方案
net.Pipe
文档说:
Pipe 创建一个同步的、内存中的、全双工的网络连接;两端都实现了 Conn 接口。一端的读取与另一端的写入匹配,直接在两者之间复制数据;没有内部缓冲。
因此,您附加到net.Conn
's (server
和client
) 的标签是任意的。如果您发现它更容易理解,请随意使用 something line handleMessageConn, sendMessageConn := net.Pipe()
。
下面基本上填写了您提到的答案中给出的示例。
func TestHandleMessage(t *testing.T) {
server, client := net.Pipe()
// Set deadline so test can detect if HandleMessage does not return
client.SetDeadline(time.Now().Add(time.Second))
// Configure a go routine to act as the server
go func() {
HandleMessage(server)
server.Close()
}()
_, err := client.Write([]byte("test\n"))
if err != nil {
t.Fatalf("failed to write: %s", err)
}
// As the go routine closes the connection ReadAll is a simple way to get the response
in, err := io.ReadAll(client)
if err != nil {
t.Fatalf("failed to read: %s", err)
}
// Using an Assert here will also work (if using a library that provides that functionality)
if string(in) != "test whatever" {
t.Fatalf("expected `test` got `%s`", in)
}
client.Close()
}
您可以扭转这种情况并将Write
/Read
放在 go 例程中,但我相信上述方法更容易理解并简化了避免测试包的限制:
测试在其 Test 函数返回或调用任何方法 FailNow、Fatal、Fatalf、SkipNow、Skip 或 Skipf 时结束。这些方法以及 Parallel 方法只能从运行 Test 函数的 goroutine 中调用。
注意:如果您不需要net.Conn
(就像这个简单示例中的情况),请考虑使用HandleMessage(c io.ReadWriter)
(这为用户提供了更大的灵活性并简化了测试)。
推荐阅读
- c# - 当我导航到其他页面并返回时,UWP Pivot 控件会清除其内容
- javascript - 如何按名称将文件上传到特定的谷歌驱动器文件夹?
- apache-spark - Databricks 作业超时并出现错误:[IP] 上的执行程序 0 丢失。远程 RPC 客户端解除关联
- java - Java中的自定义序列化
- python - 添加句号“。” 在使用 spacy NLP 进行摘要时,在每个句子行之后
- c# - 使用 LINQ 对时间戳列表中的重复项进行排序、舍入和删除
- google-cloud-functions - 如何从 Firebase Functions 使用 Google Analytics
- azure - 如何在 Azure PowerShell Runbook 上应用 SharePoint 网站模板?
- mysql - 服务器重启导致 MySQL 复制中断
- docker - 使用 docker-volum 或等效的 Openshift