没有 testify/assert 的富有表现力的测试

许多 Go 程序员更喜欢使用无 if 的测试断言,以使测试更短且更易读。例如,不用带有 t.Errorfif 语句:

func Test(t *testing.T) {
    db := getDB(t)
    db.Str().Set("name", "alice")

    age, err := db.Str().Get("age")
    if!errors.Is(err, redka.ErrNotFound) {
        t.Errorf("want ErrNotFound, got %v", err)
    }
    if age!= nil {
        t.Errorf("want nil, got %v", age)
    }
}
PASS

而是使用 testify/assert(或其“邪恶双胞胎” testify/require):

func Test(t *testing.T) {
    db := getDB(t)
    db.Str().Set("name", "alice")

    age, err := db.Str().Get("age")
    assert.ErrorIs(t, err, redka.ErrNotFound)
    assert.Nil(t, age)
}

但作者认为不需要 testify/assert 及其 40 种不同的断言函数来保持测试干净,提出了一种替代方法:

  • 断言相等:最常见的测试断言类型是检查相等性,如:

    func Test(t *testing.T) {
      db := getDB(t)
      db.Str().Set("name", "alice")
    
      name, _ := db.Str().Get("name")
      if name.String()!= "alice" {
          t.Errorf("want 'alice', got '%v'", name)
      }
    }
    PASS

    并编写了基本的通用断言辅助函数 AssertEqual

    // AssertEqual asserts that got is equal to want.
    func AssertEqual[T any](tb testing.TB, got T, want T) {
      tb.Helper()
    
      // Check if both are nil.
      if isNil(got) && isNil(want) {
          return
      }
    
      // Fallback to reflective comparison.
      if reflect.DeepEqual(got, want) {
          return
      }
    
      // No match, report the failure.
      tb.Errorf("want %#v, got %#v", want, got)
    }

    还对一些类型(如 time.Timenet.IP)进行了优化,使用其 Equal 方法进行比较,并使用 bytes.Equal 优化字节切片的比较。

  • 断言错误:在 Go 中错误无处不在,所以检查错误是测试的重要部分,如:

    func Test(t *testing.T) {
      db := getDB(t)
      db.Str().Set("name", "alice")
    
      _, err := db.Str().Get("name")
      if err!= nil {
          t.Errorf("unexpected error: %v'", err)
      }
    }
    PASS

    并创建了单独的函数 AssertErr 来处理错误断言,包括检查无错误、特定错误值、错误类型、错误消息等情况,还使 want 参数可选,以处理可能存在的任意错误。

  • 其他断言AssertEqualAssertErr 可能处理典型 Go 项目中 85 - 95%的测试断言,对于剩下的 5 - 15%,如检查长度和包含字符串等情况,引入了 AssertTrue 函数:

    // AssertTrue asserts that got is true.
    func AssertTrue(tb testing.TB, got bool) {
      tb.Helper()
      if!got {
          tb.Error("not true")
      }
    }

    最后作者认为不需要 40 种断言函数来测试 Go 应用,三个(甚至两个)就足够了,如 EqualErrTrue,并将其提取到了 github.com/nalgeon/be 迷你包中。

阅读 5
0 条评论