从测试中停止Go例程

时间:2020-07-24 11:33:03

标签: go

我有一个要向上旋转的节点,直到我决定停止它为止。我目前有一个Start方法在Contexts Done通道上阻塞,然后有一个Stop函数调用cancel,在我的测试中,我的Start似乎永远挂起,并且从未调用过Stop。我无法弄清楚为什么未调用Done信号并停止我的节点。

var (
    // Ctx is the node's main context.
    Ctx, cancel = context.WithCancel(context.Background())

    // Cancel is a function used to initiate graceful shutdown.
    Cancel = cancel
)

type (
    Node struct {
      database *store.Store
    }
)

// Starts run the Node.
func (n *Node) Start() error {
    var nodeError error

    defer func() {
        err := n.database.Close()
        if err != nil {
            nodeError = err
        }
    }()

    <-Ctx.Done()

    return nodeError
}

// Stop stops the node.
func (n *Node) Stop() {
    Cancel()
}

我的测试是:

func TestNode_Start(t *testing.T) {
    n, _ := node.NewNode("1.0")
    err := n.Start()
    n.Stop()
    assert.NoError(t, err)
}

1 个答案:

答案 0 :(得分:2)

您的代码有几个问题。让我们分解一下。

var (
    // Ctx is the node's main context.
    Ctx, cancel = context.WithCancel(context.Background())

    // Cancel is a function used to initiate graceful shutdown.
    Cancel = cancel
)

这些不应该是程序包变量。它们应该是实例变量-也就是说,成员或Node结构。通过制作这些程序包变量,如果您有多个使用Node的测试,它们将互相踩到脚趾,导致竞争状况并崩溃。因此,请执行以下操作:

type Node struct {
    database *store.Store
    ctx      context.Context
    cancel   context.CancelFunc
}

接下来,我们看到您的Start()方法中有一个延迟函数:

// Starts run the Node.
func (n *Node) Start() error {
    var nodeError error

    defer func() {
        err := n.database.Close()
        if err != nil {
            nodeError = err
        }
    }()
    /* snip */

这不符合您的期望。 Start()返回后,它将关闭数据库连接-在任何可能的机会使用它之前。

相反,您应该在Stop()方法中关闭数据库连接:

// Stop stops the node.
func (n *Node) Stop() error {
    n.cancel()
    return n.database.Close()
}

最后,您的Start()方法将阻塞,因为它等待上下文取消,该上下文在调用Stop()之前是不可能被取消的,只有在Start()返回之后才被调用:

func (n *Node) Start() error {
    /* snip */

    <-Ctx.Done()

    return nodeError
}

我完全没有想到将<-Ctx.Done中包含Start()的任何原因,因此我将其删除。

在我建议的所有更改中,您应该具有以下内容:

type Node struct {
    database *store.Store
    ctx      context.Context
    cancel   context.CancelFunc
}

// Starts run the Node.
func (n *Node) Start() {
    n.ctx, n.cancel = context.WithCancel(context.Background())
}

// Stop stops the node.
func (n *Node) Stop() error {
    n.cancel()
    return n.database.Close()
}

当然,这仍然没有解决是否使用ctx的问题。由于您的原始代码未包含该代码,因此我也没有。