douyasihefu6214 2016-06-30 13:45
浏览 30
已采纳

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

If I have code that works with a net.Conn, how can I write tests for it without actually creating a network connection to localhost?

I've seen no solutions to this online; people seem to either ignore it (no tests), write tests that cannot run in parallel (ie. use an actual network connection, which uses up ports), or use io.Pipe.

However, net.Conn defines SetReadDeadline, SetWriteDeadline; and io.Pipe doesn't. net.Pipe also doesnt, despite superficially claiming to implement the interface, it's simply implemented with:

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")}
}

(see: https://golang.org/src/net/pipe.go)

So... is there some other way of doing this?

I'll accept any answer that shows how to use a stream in a test with a working deadline that is not an actual network socket.

(Idly, this cloudflare blogpost covers the motivation for using deadlines, and why blocking forever in a goroutine per connection is not an acceptable solution; but regardless of that argument, particularly in this case I'm looking for a solution for tests where we deliberately want to handle edge cases where a bad connection hangs, etc.)

(NB. this may seem like a duplicate of Simulate a tcp connection in Go, but notice that all the solutions proposed in that question do not implement the Deadline functions, which is specifically what I'm asking about how to test here)

  • 写回答

2条回答 默认 最新

  • douqie1816 2016-07-05 08:29
    关注

    Your question is very open, so it is not possible to give you "the correct answer". But I think I understand the point where you stuck. This answer is also open, but it should bring you back on the right track.

    A few days ago I wrote a short article, which shows the principle which you have to use.

    Before I make some small examples how you such tests work, we need to fix one important point:

    We do not test the net package. We asume, that the package has no bug and does, what the documentation says. That means we do not care how the Go team has implementet SetReadDeadline and SetWriteDeadline. We test only the usage in our programm.

    Step 1: Refactoring your code

    You did not post any code snippets, so I give you just a simple example. I guess you have a method or function, where you are using the net package.

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

    That you are able to to test you need to refactor your function, so it is just using the net.Conn interface. To do this, the net.Dial() call has to be moved outside of the function. Remember that we don't want to test the net.Dial function.

    The new function could look something like that:

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

    Step 2: Implement the net.Conn interface

    For testing you need to implement the net.Conn interface:

    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
    }
    
    ...
    

    Complete implementation including a small type check: https://play.golang.org/p/taAmI61vVz

    Step 3: Testing

    When testing, we don't care about the Dial() Method, we just create a pointer to our testtype, which implements the net.Conn interface and put it into your function. Afterwards we look inside our test cases, if the deadline parameter is set correctly.

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

    So when testing, you should always think about, what feature you want to test. I think it is the really most difficult part to abstract the functionality you really want to write. Inside of simple unit tests you should never test functionalities of the standard library. Hope this examples can help you to bring you back on the right track.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 ensp路由器启动不了一直报#
  • ¥50 安卓10如何在没有root权限的情况下设置开机自动启动指定app?
  • ¥15 ats2837 spi2从机的代码
  • ¥200 wsl2 vllm qwen1.5部署问题
  • ¥100 有偿求数字经济对经贸的影响机制的一个数学模型,弄不出来已经快要碎掉了
  • ¥15 数学建模数学建模需要
  • ¥15 已知许多点位,想通过高斯分布来随机选择固定数量的点位怎么改
  • ¥20 nao机器人语音识别问题
  • ¥15 怎么生成确定数目的泊松点过程
  • ¥15 layui数据表格多次重载的数据覆盖问题