为了账号安全,请及时绑定邮箱和手机立即绑定

如何对修改发送消息的 net.Conn 函数进行单元测试?

如何对修改发送消息的 net.Conn 函数进行单元测试?

Go
森栏 2022-10-10 15:49:25
首先,让我告诉我,我已经看到了与这个类似的其他问题,但我认为没有一个问题能真正详细地回答它(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 功能时可能指向正确的方向。
查看完整描述

2 回答

?
一只萌萌小番薯

TA贡献1795条经验 获得超7个赞

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)(这为用户提供了更大的灵活性并简化了测试)。


查看完整回答
反对 回复 2022-10-10
?
收到一只叮咚

TA贡献1821条经验 获得超5个赞

块并且根本不会完成


好吧,您发布的代码是如此不完整,以至于我无法知道这些是否是您面临的真正问题-例如,没有c. TestHandleMessage但是给你怀疑的好处,我会说这段代码有两个问题:


首先,您的测试从不写入,client因此没有可读取的内容server。您写入server并关闭它,但您从不向client. 所以这是第一个问题,但同样,该代码不会编译。


其次,看这个组合:


  c.Write([]byte(s + " whatever"))

  s, err := bufio.NewReader(c).ReadString('\n')

HandleMessage不写换行符,但客户需要一个。它无限期地挂起,等待永远不会被写入的换行符。同样,我不确定您是否遇到了这个问题或第一个问题或两者兼而有之 - 因为您的代码无法编译。


您必须更改此行:


  c.Write([]byte(s + " whatever\n"))

您还必须将初始字符串写入客户端连接,以便管道另一端可以读取它server。


将它们放在一起,提取任何外部依赖项(作证),并修复一些错误最终得到:


t.go:


package main


import (

    "net"

    "fmt"

    "bufio"

    "strings"

)


func HandleMessage(c net.Conn) {

  s, err := bufio.NewReader(c).ReadString('\n')

    if err != nil {

        panic(fmt.Errorf("HandleMessage could not read: %w", err))

    }

  s = strings.Trim(s, "\n")

  c.Write([]byte(s + " whatever\n"))

}

t_test.go:


package main


import(

    "net"

    "bufio"

    "fmt"

    "testing"

)


func TestHandleMessage(t *testing.T) {

  server, client := net.Pipe()


  go func(){

    HandleMessage(server)

  }()

    fmt.Fprintln(client, "test")

  s, err := bufio.NewReader(client).ReadString('\n')


  if err != nil {

        t.Errorf("Error should be nil, got: %s", err.Error())

    }

    if s != "test whatever\n" {

        t.Errorf("Expected result to be 'test whatever\\n', got %s", s)

    }

}

% go test t.go t_test.go -v

=== RUN   TestHandleMessage

--- PASS: TestHandleMessage (0.00s)

PASS

ok      command-line-arguments  0.100s


查看完整回答
反对 回复 2022-10-10
  • 2 回答
  • 0 关注
  • 132 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号