首页 > 解决方案 > 如何对修改发送消息的 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 功能时可能指向正确的方向。

标签: unit-testinggotestingtcp

解决方案


net.Pipe 文档说:

Pipe 创建一个同步的、内存中的、全双工的网络连接;两端都实现了 Conn 接口。一端的读取与另一端的写入匹配,直接在两者之间复制数据;没有内部缓冲。

因此,您附加到net.Conn's (serverclient) 的标签是任意的。如果您发现它更容易理解,请随意使用 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)(这为用户提供了更大的灵活性并简化了测试)。


推荐阅读