验证模拟调用的顺序
Verify order of mocks call
我使用 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
}
虽然存在未解决的问题 (stretchr/testify/issue 741 "assert mock calls in order")
,但并未直接支持此功能
更普遍的问题684 "Assert call order?"包括反驳:
IMO you should check the output of the function but not how it works internally. It may lead to testing implementation which is very hard to maintain.
虽然,在同一个线程中:
IMO there are cases to enforce order. i.e if you mock a mutex, you better check that Lock was always called before Unlock.
We can have a simple implementation where the mock has an "assertExpectationsInOrder
" true/false flag that can be set before any expectations are added.
这可能会导致一些测试,例如 cassandra-operator/cmd/operator/controller_test.go
记录事件以测试他们的订单。
IMO there are cases to enforce order. i.e if you mock a mutex, you better check that Lock was always called before 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)
}
如果有原因,可以将订单检查逻辑复杂化...
正如其他人所说,这是一个内部细节,确实会使您的测试与实现纠缠在一起。如果您确定顺序很重要,这就没有任何意义。在这里保持简洁是另一种使用最低限度测试顺序的解决方案。
创建两个实现 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()
}
}
}
我使用 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
}
虽然存在未解决的问题 (stretchr/testify/issue 741 "assert mock calls in order")
,但并未直接支持此功能更普遍的问题684 "Assert call order?"包括反驳:
IMO you should check the output of the function but not how it works internally. It may lead to testing implementation which is very hard to maintain.
虽然,在同一个线程中:
IMO there are cases to enforce order. i.e if you mock a mutex, you better check that Lock was always called before Unlock.
We can have a simple implementation where the mock has an "assertExpectationsInOrder
" true/false flag that can be set before any expectations are added.
这可能会导致一些测试,例如 cassandra-operator/cmd/operator/controller_test.go
记录事件以测试他们的订单。
IMO there are cases to enforce order. i.e if you mock a mutex, you better check that Lock was always called before 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)
}
如果有原因,可以将订单检查逻辑复杂化...
正如其他人所说,这是一个内部细节,确实会使您的测试与实现纠缠在一起。如果您确定顺序很重要,这就没有任何意义。在这里保持简洁是另一种使用最低限度测试顺序的解决方案。
创建两个实现 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()
}
}
}