一尘不染

如何在不创建实际网络连接的情况下测试依赖net.Conn的代码?

go

如果我有与一起使用的代码net.Conn,如何在不实际创建与localhost的网络连接的情况下为其编写测试?

我在网上没有看到任何解决方案。人们似乎忽略了它(没有测试),编写了不能并行运行的测试(即使用实际的网络连接,使用了端口),或者使用了io.Pipe。

然而,net.Conn限定SetReadDeadlineSetWriteDeadline; 而io.Pipe没有。net.Pipe
没有,尽管从表面上声称实现了该接口,但它只是通过以下方式实现:

func (p *pipe) SetDeadline(t time.Time) error {
    return &OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}

func (p *pipe) SetReadDeadline(t time.Time) error {
    return &OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}

func (p *pipe) SetWriteDeadline(t time.Time) error {
    return &OpError{Op: "set", Net: "pipe", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}

(请参阅:https :
//golang.org/src/net/pipe.go)

那么…还有其他方法可以做到吗?

我将接受任何显示如何在测试截止时间(不是实际的网络套接字)中使用流的答案。

理想情况下,此cloudflare博客文章涵盖了使用截止日期的动机,以及为什么在每个连接的goroutine中永远阻塞不是可接受的解决方案;但是不管那种说法如何,尤其是在这种情况下,我正在寻找一种
针对测试 的解决方案,我们故意想要处理连接不良的情况下等)

(注意。这似乎是Go中Simulatetcp连接的复制品,但请注意,该问题中提出的所有解决方案均未实现Deadline函数,这正是我要问的此处测试方法)


阅读 229

收藏
2020-07-02

共1个答案

一尘不染

您的问题很开放,因此不可能给您“正确答案”。但是我想我理解您遇到的困难。这个答案也是开放的,但是它应该使您回到正确的轨道上。

几天前,我写了一篇简短的文章,其中显示了您必须使用的原理。

在我举一些小例子说明您的测试如何工作之前,我们需要修正一个重要点:

我们 不测试网络包装 。我们假设 该文件包没有错误 ,但确实如此。这意味着 我们不在乎Go团队如何实现
SetReadDeadline实现SetWriteDeadline。我们仅在程序中测试用法。

步骤1:重构代码

您没有发布任何代码段,因此我仅举一个简单的示例。我猜你有一个方法或函数,正在使用net包。

func myConn(...) error {
  // You code is here
  c, err := net.Dial("tcp", "12.34.56.78:80")
  c.setDeadline(t)
  // More code here
}

能够测试您需要重构功能的功能,因此它仅使用net.Conn接口。为此,必须将net.Dial()调用移到函数之外。请记住,我们不想测试net.Dial函数。

新功能可能如下所示:

func myConn(c, net.Conn, ...) error {
  // You code is here
  c.setDeadline(t)
  // More code here
}

步骤2:实现net.Conn接口

为了进行测试,您需要实现net.Conn接口:

type connTester struct {
    deadline time.Time
}

func (c *connTester) Read(b []byte) (n int, err error) {
    return 0, nil
}

...

func (c *connTester) SetDeadline(t time.Time) error {
    c.deadline = t
    return nil
}

...

完整的实现,包括小的类型检查:https :
//play.golang.org/p/taAmI61vVz

步骤3:测试

在测试时,我们并不关心Dial()方法,我们只是创建一个指向我们的测试类型的指针,该指针实现了net.Conn接口并将其放入您的函数中。然后,如果最后期限参数设置正确,我们将查看测试用例。

func TestMyConn(t *testing.T){
  myconnTester = &connTester{}
  err := myConn(myconnTester,...)
  ...
  if myconntester.deadline != expectedDeadline{
  //Test fails
  }
}

因此,在测试时,您应该始终考虑要测试的功能。我认为对您真正想要编写的功能进行抽象是最困难的部分。在简单的单元测试中,您永远不要测试标准库的功能。希望这个例子可以帮助您重新走上正轨。

2020-07-02