如何对修改发送消息的 net.Conn 函数进行单元测试?
How to unit test a net.Conn function that modifies the message sent?
首先,让我告诉你,我已经看到了与这个类似的其他问题,但我认为没有任何问题真正足够详细地回答它 ( and )。
我想测试的是以某种方式响应 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)?
放在哪里才能真正获得我想要的响应。我可以做到这一点块并且根本不会完成,或者它将 return 与我在服务器中写入的字符串完全相同。
谁能帮我指出我哪里出错了?或者在测试 TCP 功能时指出正确的方向。
net.Pipe
docs say:
Pipe creates a synchronous, in-memory, full duplex network connection; both ends implement the Conn interface. Reads on one end are matched with writes on the other, copying data directly between the two; there is no internal buffering.
所以您附加到 net.Conn
的标签(server
和 client
)是任意的。如果您发现它更容易理解,请随意使用行 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 例程中,但我相信上面的方法更容易理解并且简化了避免测试包的 a limitation :
A test ends when its Test function returns or calls any of the methods FailNow, Fatal, Fatalf, SkipNow, Skip, or Skipf. Those methods, as well as the Parallel method, must be called only from the goroutine running the Test function.
注意:如果您不需要 net.Conn
(如这个简单示例中的情况),请考虑使用 HandleMessage(c io.ReadWriter)
(这为用户提供了更大的灵活性并简化了测试)。
blocks and won't finish at all
好吧,您发布的代码非常不完整,以至于我无法知道这些是否是您面临的真正问题 - 例如,TestHandleMessage
中没有 c
。但是为了让你免于怀疑,我会说这段代码有 2 个问题:
首先,您的测试从不写入 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
首先,让我告诉你,我已经看到了与这个类似的其他问题,但我认为没有任何问题真正足够详细地回答它 (
我想测试的是以某种方式响应 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)?
放在哪里才能真正获得我想要的响应。我可以做到这一点块并且根本不会完成,或者它将 return 与我在服务器中写入的字符串完全相同。
谁能帮我指出我哪里出错了?或者在测试 TCP 功能时指出正确的方向。
net.Pipe
docs say:
Pipe creates a synchronous, in-memory, full duplex network connection; both ends implement the Conn interface. Reads on one end are matched with writes on the other, copying data directly between the two; there is no internal buffering.
所以您附加到 net.Conn
的标签(server
和 client
)是任意的。如果您发现它更容易理解,请随意使用行 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 例程中,但我相信上面的方法更容易理解并且简化了避免测试包的 a limitation :
A test ends when its Test function returns or calls any of the methods FailNow, Fatal, Fatalf, SkipNow, Skip, or Skipf. Those methods, as well as the Parallel method, must be called only from the goroutine running the Test function.
注意:如果您不需要 net.Conn
(如这个简单示例中的情况),请考虑使用 HandleMessage(c io.ReadWriter)
(这为用户提供了更大的灵活性并简化了测试)。
blocks and won't finish at all
好吧,您发布的代码非常不完整,以至于我无法知道这些是否是您面临的真正问题 - 例如,TestHandleMessage
中没有 c
。但是为了让你免于怀疑,我会说这段代码有 2 个问题:
首先,您的测试从不写入 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