【问题标题】:Verify order of mocks call验证模拟调用的顺序
【发布时间】:2020-08-05 03:49:45
【问题描述】:

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

例子:

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
}

【问题讨论】:

    标签: go mocking testify


    【解决方案1】:

    即使存在未解决的问题 (stretchr/testify/issue 741 "assert mock calls in order"),也不直接支持此功能

    更普遍的问题684 "Assert call order?" 包括反驳:

    IMO 你应该检查函数的输出,而不是它内部的工作方式。这可能会导致很难维护的测试实现。

    虽然,在同一个线程中:

    IMO 有强制执行命令的情况。也就是说,如果你模拟一个互斥锁,你最好检查一下 Lock 总是在 Unlock 之前被调用。
    我们可以有一个简单的实现,其中模拟有一个“assertExpectationsInOrder”真/假标志,可以在添加任何期望之前设置。

    这可能会导致一些测试,例如 cassandra-operator/cmd/operator/controller_test.go,它会记录事件以测试他们的订单。

    【讨论】:

      【解决方案2】:

      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

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

      【讨论】:

      • 这看起来是我提到的想法的一个很好的实现。赞成。
      【解决方案3】:

      正如其他人所说,这是一个内部细节,并且确实将您的测试与实现纠缠在一起。如果您确定订单很重要,这将毫无意义。在这里保持简洁是另一种使用最低限度测试订单的解决方案。

      创建两个实现 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

      【讨论】:

      • 请把例子重写为测试
      • @kozmo 对于测试,只需将 main 替换为测试功能。我使用示例测试函数更新了解决方案。
      猜你喜欢
      • 2018-08-26
      • 1970-01-01
      • 2014-03-21
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 2015-12-30
      • 2015-06-27
      • 2016-01-18
      相关资源
      最近更新 更多