Go语言 验证模拟调用的顺序

8zzbczxx  于 2022-12-16  发布在  Go
关注(0)|答案(4)|浏览(130)

我使用的是testify(v1.6.1),需要测试接口的方法调用顺序是否正确。我检查了documentation,并试图在互联网上找到任何信息,但没有找到任何关于mocks顺序检查的信息。
示例:

type InterfaceA interface {
    Execute()
}

type InterfaceB interface {
    Execute()
}
type Composition struct {
    a InterfaceA
    b InterfaceB
}

func (c * Composition) Apply() error {
    //How to check that "a" execute before "b"?
    c.a.Execute()
    c.b.Execute()
    return nil
}
yzxexxkh

yzxexxkh1#

不直接支持此功能,即使存在未决问题(stretchr/testify/issue 741 "assert mock calls in order"
更普遍的问题684 "Assert call order?"包括反驳:
IMO你应该检查函数的输出,而不是它内部是如何工作的。这可能会导致测试实现,这是非常难以维护的。
不过,在同一条线索中:
IMO有强制执行命令的情况。也就是说,如果你模拟一个互斥体,你最好检查一下Lock总是在Unlock之前被调用。
我们可以有一个简单的实现,其中mock有一个“assertExpectationsInOrder“true/false标志,可以在添加任何期望之前设置该标志。
这可能会导致一些测试,如cassandra-operator/cmd/operator/controller_test.go,它记录事件以测试它们的顺序。

vmdwslir

vmdwslir2#

VonC答复后,将拉取请求合并到相关问题https://github.com/stretchr/testify/pull/1106

现在可以通过以下方式实现:

call1 := mockInterfaceA.On("Execute").Return(nil)
call2 := mockInterfaceB.On("Execute").Return(nil).NotBefore(call1)

comp := Composition{mockInterfaceA, mockInterfaceB}
comp.Apply()

如果没有按照预期的顺序调用它们,测试将出现异常并显示相关消息

panic: mock: Unexpected Method Call
-----------------------------

Apply()
        0: ...

Must not be called before:

Execute()
yvt65v4c

yvt65v4c3#

IMO有强制执行命令的情况。也就是说,如果你模拟一个互斥体,你最好检查一下Lock总是在Unlock之前被调用。
这是一个简单的单线程实现:

func TestOrderOfMocks(t *testing.T) {
    order := 0
    amock := new(AMock)
    amock.On("Execute").Run(func(args mock.Arguments) {
        if order++; order != 1 {
            t.Fail()
        }
    })

    bmock := new(BMock)
    bmock.On("Execute").Run(func(args mock.Arguments) {
        if order++; order != 2 {
            t.Fail()
        }
    })

    c := &Composition{amock, bmock}
    err := c.Apply()
    require.NoError(t, err)
}

PLAYGROUND
如果有原因,您可以使订单检查逻辑复杂化...

ccgok5k5

ccgok5k54#

正如其他人所说的,这是一个内部细节,并且确实会使您的测试与实现纠缠在一起。如果您已经确定顺序很重要,这就没有任何意义。在这里保持事情简洁是另一种解决方案,使用测试顺序的最低限度。
创建两个实现InterfaceA和InterfaceB的间谍。

type SpyA struct {
    Calls *[]string
}

func (s *SpyA) Execute() {
    *s.Calls = append(*s.Calls, "ExecuteA")
}

type SpyB struct {
    Calls *[]string
}

func (s *SpyB) Execute() {
    *s.Calls = append(*s.Calls, "ExecuteB")
}

那就这样用吧。

func TestApply(t *testing.T) {
    got := []string{}
    c := &Composition{
        a: &SpyA{&got},
        b: &SpyB{&got},
    }

    c.Apply()

    expected := []string{"ExecuteA", "ExecuteB"}

    if len(got) != len(expected) {
        t.Fail()
    }

    for i := range got {
        if got[i] != expected[i] {
            t.Fail()
        }
    }
}

Playground

相关问题