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条)

报告相同问题?

悬赏问题

  • ¥35 平滑拟合曲线该如何生成
  • ¥100 c语言,请帮蒟蒻写一个题的范例作参考
  • ¥15 名为“Product”的列已属于此 DataTable
  • ¥15 安卓adb backup备份应用数据失败
  • ¥15 eclipse运行项目时遇到的问题
  • ¥15 关于#c##的问题:最近需要用CAT工具Trados进行一些开发
  • ¥15 南大pa1 小游戏没有界面,并且报了如下错误,尝试过换显卡驱动,但是好像不行
  • ¥15 自己瞎改改,结果现在又运行不了了
  • ¥15 链式存储应该如何解决
  • ¥15 没有证书,nginx怎么反向代理到只能接受https的公网网站