Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Unit testing 如何在不创建实际网络连接的情况下测试依赖于net.Conn的代码?_Unit Testing_Go - Fatal编程技术网

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

Unit testing 如何在不创建实际网络连接的情况下测试依赖于net.Conn的代码?,unit-testing,go,Unit Testing,Go,如果我有与net.Conn一起工作的代码,我如何在不实际创建到本地主机的网络连接的情况下为它编写测试 我在网上没有看到解决这个问题的方法;人们似乎要么忽略它(没有测试),要么编写无法并行运行的测试(即使用实际的网络连接,这会耗尽端口),要么使用io.Pipe 但是,net.Conn定义了SetReadDeadline,SetWriteDeadline;伊奥。派普没有。net.Pipe也没有,尽管表面上声称实现了接口,但它只是通过以下方式实现的: func (p *pipe) SetDeadlin

如果我有与
net.Conn
一起工作的代码,我如何在不实际创建到本地主机的网络连接的情况下为它编写测试

我在网上没有看到解决这个问题的方法;人们似乎要么忽略它(没有测试),要么编写无法并行运行的测试(即使用实际的网络连接,这会耗尽端口),要么使用io.Pipe

但是,
net.Conn
定义了
SetReadDeadline
SetWriteDeadline
;伊奥。派普没有。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")}
}
(见:)

所以。。。还有别的方法吗

我将接受任何显示如何在工作期限不是实际网络套接字的测试中使用流的答案

(毫无疑问,这涵盖了使用截止日期的动机,以及为什么每次连接在goroutine中永远阻塞不是一个可接受的解决方案;但是不管这个论点如何,特别是在这种情况下,我正在寻找一个解决方案,用于测试,我们有意处理挂起坏连接的边缘情况,等等。)


(注意,这看起来像是重复的,但请注意,在这个问题中提出的所有解决方案都没有实现截止日期函数,这正是我在这里要问的关于如何测试的问题)

对于单元测试中的受控版本,需要交换的代码应该存在于抽象之后。在这种情况下,抽象将是
net.Conn
接口。生产代码将使用go-std-lib
net.Conn
,但测试代码将使用配置了精确逻辑的测试存根来执行您的功能

引入抽象是一种强大的模式,它应该允许在单元测试期间交换所有IO或基于时间的代码,以允许代码的受控执行

@apxp在评论中也说明了同样的方法


同样的方法也适用于截止日期。模拟截止日期可能会变得很棘手,因为您可能需要配置具有多个响应的存根。即,第一个响应成功,但第二个响应模拟已达到的截止日期,并为第二个请求抛出错误

你的问题非常开放,因此不可能给你“正确答案”。但我想我明白你的意思。这个答案也是开放的,但它应该会让你回到正确的轨道上

几天前,我写了一篇文章,展示了你必须使用的原则

在我举一些小例子说明这些测试是如何工作的之前,我们需要解决一个重要问题:

我们不测试网络包。我们知道,包没有bug,并且按照文档中的说明执行。这意味着我们不关心Go团队如何实现et
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
}

...
完整实施,包括小型类型检查:

步骤3:测试

在测试时,我们不关心
Dial()
方法,我们只是创建一个指向testtype的指针,它实现了net.Conn接口并将其放入函数中。之后,我们查看测试用例内部,看是否正确设置了deadline参数

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

因此,在测试时,您应该始终考虑要测试的功能。我认为抽象出您真正想要编写的功能是最困难的部分。在简单的单元测试中,永远不要测试标准库的功能。希望这些示例能帮助您回到正确的轨道。

为什么不能使用网络连接?大多数需要net.Conn的测试都使用一个,通常连接到测试控制下的另一个本地端点。您必须检查网卡的日志(以太网/wifi…)。当您进行许多测试时,每次测试使用一个实际的网络端口肯定不成比例吗?(并行运行…)您会做什么,选择一个随机端口并希望?分配一个端口池并乐观地绑定端口,直到找到一个空闲端口为止?您让操作系统为您绑定端口。第一步:重构,您的代码仅使用Conn接口()第二步:实现用于测试的Conn接口第三步:将实现中的值与预期值进行比较。能否更具体地说明截止日期?我看不出如何实际应用这个建议。我能看到的唯一可行的方法似乎是像fork net.Pipe这样的东西,并以某种方式实现假截止日期?但我不知道你是怎么做到的。。。