8

在 Go 中,一个 TCP 连接(net.Conn)是一个 io.ReadWriteCloser。我想通过模拟 TCP 连接来测试我的网络代码。我有两个要求:

  1. 要读取的数据存储在字符串中
  2. 每当写入数据时,我都希望将其存储在某种缓冲区中,以便以后访问

是否有用于此的数据结构,或者一种简单的方法?

4

4 回答 4

5

编辑:我已将此答案整合到一个包中,这使事情变得更简单 - 请参见此处:https ://github.com/jordwest/mock-conn


虽然 Ivan 的解决方案适用于简单的情况,但请记住,真正的 TCP 连接实际上是两个缓冲区,或者更确切地说是管道。例如:

 Server   |   Client
 ---------+---------
  reads <===  writes
 writes ===>  reads

如果您使用服务器读取和写入的单个缓冲区,您最终可能会导致服务器与自身对话。

这是一个允许您将MockConn类型作为 a传递ReadWriteCloser给服务器的解决方案。,Read和函数只是代理到管道服务器端的函数WriteClose

type MockConn struct {
    ServerReader *io.PipeReader
    ServerWriter *io.PipeWriter

    ClientReader *io.PipeReader
    ClientWriter *io.PipeWriter
}

func (c MockConn) Close() error {
    if err := c.ServerWriter.Close(); err != nil {
        return err
    }
    if err := c.ServerReader.Close(); err != nil {
        return err
    }
    return nil
}

func (c MockConn) Read(data []byte) (n int, err error)  { return c.ServerReader.Read(data) }
func (c MockConn) Write(data []byte) (n int, err error) { return c.ServerWriter.Write(data) }

func NewMockConn() MockConn {
    serverRead, clientWrite := io.Pipe()
    clientRead, serverWrite := io.Pipe()

    return MockConn{
        ServerReader: serverRead,
        ServerWriter: serverWrite,
        ClientReader: clientRead,
        ClientWriter: clientWrite,
    }
}

模拟“服务器”连接时,只需传递 MockConn 代替您将使用的位置net.Conn(这显然只实现了接口,如果您需要支持完整的接口ReadWriteCloser,您可以轻松地为 etc 添加虚拟方法)LocalAddr()net.Conn

在您的测试中,您可以根据需要通过读取和写入ClientReaderandClientWriter字段来充当客户端:

func TestTalkToServer(t *testing.T) {
    /*
     * Assumes that NewMockConn has already been called and
     * the server is waiting for incoming data
     */

    // Send a message to the server
    fmt.Fprintf(mockConn.ClientWriter, "Hello from client!\n")

    // Wait for the response from the server
    rd := bufio.NewReader(mockConn.ClientReader)
    line, err := rd.ReadString('\n')

    if line != "Hello from server!" {
        t.Errorf("Server response not as expected: %s\n", line)
    }
}
于 2015-06-23T08:12:21.600 回答
5

不知道问这个问题时是否存在,但您可能想要它为您提供两个相互链接的net.Pipe()全双工实例net.Conn

于 2015-11-29T18:58:14.953 回答
4

为什么不使用bytes.Buffer?它是一种获取存储数据io.ReadWriter的方法。String如果您需要将其设为io.ReadWriteCloser,您可以定义自己的类型:

type CloseableBuffer struct {
    bytes.Buffer
}

并定义一个Close方法:

func (b *CloseableBuffer) Close() error {
    return nil
}
于 2009-12-30T17:39:52.127 回答
0

大多数情况下您不需要模拟 net.Conn。

你总是可以在你的测试代码中运行一个 goroutine 来模仿对方。

于 2020-10-03T16:01:23.600 回答