Repository: cloudwego/hertz
Pull request #1315 (2025-03-27 10:39:31)

fix(assert): NotNil checks (*T)(nil)
#### What type of PR is this?
fix

<!--
Add one of the following kinds:

build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
docs: Documentation only changes
feat: A new feature
optimize: A new optimization
fix: A bug fix
perf: A code change that improves performance
refactor: A code change that neither fixes a bug nor adds a feature
style: Changes that do not affect the meaning of the code (white space, formatting, missing semi-colons, etc)
test: Adding missing tests or correcting existing tests
chore: Changes to the build process or auxiliary tools and libraries such as documentation generation
-->

#### Check the PR title.
<!--
The description of the title will be attached in Release Notes, 
so please describe it from user-oriented, what this PR does / why we need it.
Please check your PR title with the below requirements:
-->
- [ ] This PR title match the format: \<type\>(optional scope): \<description\>
- [ ] The description of this PR title is user-oriented and clear enough for others to understand.
- [ ] Attach the PR updating the user documentation if the current PR requires user awareness at the usage level. [User docs repo](https://github.com/cloudwego/cloudwego.github.io)


#### (Optional) Translate the PR title into Chinese.


#### (Optional) More detailed description for this PR(en: English/zh: Chinese).
<!--
Provide more detailed info for review(e.g., it's recommended to provide perf data if this is a perf type PR).
-->
en:
zh(optional):

#### (Optional) Which issue(s) this PR fixes:
<!--
Automatically closes linked issue when PR is merged.
Eg: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->

#### (Optional) The PR that updates user documentation:
<!--
If the current PR requires user awareness at the usage level, please submit a PR to update user docs. [User docs repo](https://github.com/cloudwego/cloudwego.github.io)
-->

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/cloudwego/hertz/pull/1315?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 72.78%. Comparing base [(`576bc6f`)](https://app.codecov.io/gh/cloudwego/hertz/commit/576bc6f962a130e5bb7d66f337131953e2d60b58?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) to head [(`fac5e4e`)](https://app.codecov.io/gh/cloudwego/hertz/commit/fac5e4e9745ab5f045e9cf1ed3bdb3aa729b3124?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego).
> Report is 2 commits behind head on develop.

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##           develop    #1315      +/-   ##
===========================================
+ Coverage    72.38%   72.78%   +0.39%     
===========================================
  Files          139      139              
  Lines        16235    16237       +2     
===========================================
+ Hits         11752    11818      +66     
+ Misses        3888     3824      -64     
  Partials       595      595              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/cloudwego/hertz/pull/1315?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>

Patch:
diff --git a/go.mod b/go.mod
index a86e7ec..3469bd7 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module github.com/cloudwego/hertz
 
-go 1.17
+go 1.19
 
 require (
 	github.com/bytedance/gopkg v0.1.0


Test patch:
diff --git a/pkg/common/test/assert/assert.go b/pkg/common/test/assert/assert.go
index c82d84c..dbd9caf 100644
--- a/pkg/common/test/assert/assert.go
+++ b/pkg/common/test/assert/assert.go
@@ -18,11 +18,16 @@ package assert
 
 import (
 	"reflect"
-	"testing"
 )
 
+type testingT interface {
+	Helper()
+	Fatal(args ...any)
+	Fatalf(format string, args ...any)
+}
+
 // Assert .
-func Assert(t testing.TB, cond bool, val ...interface{}) {
+func Assert(t testingT, cond bool, val ...interface{}) {
 	t.Helper()
 	if !cond {
 		if len(val) > 0 {
@@ -35,7 +40,7 @@ func Assert(t testing.TB, cond bool, val ...interface{}) {
 }
 
 // Assertf .
-func Assertf(t testing.TB, cond bool, format string, val ...interface{}) {
+func Assertf(t testingT, cond bool, format string, val ...interface{}) {
 	t.Helper()
 	if !cond {
 		t.Fatalf(format, val...)
@@ -43,60 +48,65 @@ func Assertf(t testing.TB, cond bool, format string, val ...interface{}) {
 }
 
 // DeepEqual .
-func DeepEqual(t testing.TB, expected, actual interface{}) {
+func DeepEqual(t testingT, expected, actual interface{}) {
 	t.Helper()
 	if !reflect.DeepEqual(actual, expected) {
 		t.Fatalf("assertion failed, unexpected: %v, expected: %v", actual, expected)
 	}
 }
 
-func Nil(t testing.TB, data interface{}) {
-	t.Helper()
-	if data == nil {
-		return
-	}
-	if !reflect.ValueOf(data).IsNil() {
-		t.Fatalf("assertion failed, unexpected: %v, expected: nil", data)
+func isNil(rv reflect.Value) bool {
+	switch rv.Kind() {
+	case reflect.Chan, reflect.Func, reflect.Map, reflect.Pointer,
+		reflect.UnsafePointer, reflect.Interface, reflect.Slice:
+		if rv.IsNil() {
+			return true
+		}
 	}
+	return false
 }
 
-func NotNil(t testing.TB, data interface{}) {
+func Nil(t testingT, data interface{}) {
 	t.Helper()
-	if data != nil {
+	if data == nil || isNil(reflect.ValueOf(data)) {
 		return
 	}
+	t.Fatalf("assertion failed, unexpected: %v, expected: nil", data)
+}
 
-	if reflect.ValueOf(data).IsNil() {
+func NotNil(t testingT, data interface{}) {
+	t.Helper()
+	if data == nil || isNil(reflect.ValueOf(data)) {
 		t.Fatalf("assertion failed, unexpected: %v, expected: not nil", data)
 	}
 }
 
 // NotEqual .
-func NotEqual(t testing.TB, expected, actual interface{}) {
+func NotEqual(t testingT, expected, actual interface{}) {
 	t.Helper()
 	if expected == nil || actual == nil {
 		if expected == actual {
-			t.Fatalf("assertion failed, unexpected: %v, expected: %v", actual, expected)
+			t.Fatalf("assertion failed: %v == %v", actual, expected)
 		}
 	}
 
 	if reflect.DeepEqual(actual, expected) {
-		t.Fatalf("assertion failed, unexpected: %v, expected: %v", actual, expected)
+		t.Fatalf("assertion failed: %v == %v", actual, expected)
 	}
 }
 
-func True(t testing.TB, obj interface{}) {
+func True(t testingT, obj interface{}) {
 	t.Helper()
 	DeepEqual(t, true, obj)
 }
 
-func False(t testing.TB, obj interface{}) {
+func False(t testingT, obj interface{}) {
 	t.Helper()
 	DeepEqual(t, false, obj)
 }
 
 // Panic .
-func Panic(t testing.TB, fn func()) {
+func Panic(t testingT, fn func()) {
 	t.Helper()
 	defer func() {
 		if err := recover(); err == nil {
@@ -107,11 +117,11 @@ func Panic(t testing.TB, fn func()) {
 }
 
 // NotPanic .
-func NotPanic(t testing.TB, fn func()) {
+func NotPanic(t testingT, fn func()) {
 	t.Helper()
 	defer func() {
 		if err := recover(); err != nil {
-			t.Fatal("assertion failed: did panic")
+			t.Fatal("assertion failed: panicked")
 		}
 	}()
 	fn()
diff --git a/pkg/common/test/assert/assert_test.go b/pkg/common/test/assert/assert_test.go
new file mode 100644
index 0000000..0f5df2d
--- /dev/null
+++ b/pkg/common/test/assert/assert_test.go
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2022 CloudWeGo Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package assert
+
+import (
+	"fmt"
+	"strings"
+	"testing"
+)
+
+type mockTestingT struct {
+	fatalstr string
+}
+
+func (mockTestingT) Helper() {}
+
+func (m *mockTestingT) Reset() {
+	m.fatalstr = ""
+}
+
+func (m *mockTestingT) Fatal(args ...any) {
+	m.fatalstr = fmt.Sprintln(args...)
+}
+
+func (m *mockTestingT) Fatalf(fm string, args ...any) {
+	m.fatalstr = fmt.Sprintf(fm, args...)
+}
+
+func (m *mockTestingT) String() string {
+	return m.fatalstr
+}
+
+func (m *mockTestingT) Expect(t *testing.T, s string) {
+	t.Helper()
+	got := strings.TrimSpace(m.fatalstr)
+	if got != s {
+		t.Fatalf("got: %q expect: %q", got, s)
+	}
+	m.Reset()
+}
+
+func TestAssert(t *testing.T) {
+	m := &mockTestingT{}
+	Assert(m, true)
+	m.Expect(t, "")
+
+	Assert(m, false)
+	m.Expect(t, "assertion failed")
+
+	Assert(m, false, "hello")
+	m.Expect(t, "assertion failed: hello")
+
+	Assertf(m, true, "hello %s", "world")
+	m.Expect(t, "")
+	Assertf(m, false, "hello %s", "world")
+	m.Expect(t, "hello world")
+}
+
+func TestNil(t *testing.T) {
+	m := &mockTestingT{}
+
+	Nil(m, nil)
+	m.Expect(t, "")
+	Nil(m, (*testing.T)(nil))
+	m.Expect(t, "")
+
+	Nil(m, 1)
+	m.Expect(t, "assertion failed, unexpected: 1, expected: nil")
+
+	Nil(m, "hello")
+	m.Expect(t, "assertion failed, unexpected: hello, expected: nil")
+
+	NotNil(m, 1)
+	m.Expect(t, "")
+
+	NotNil(m, "hello")
+	m.Expect(t, "")
+
+	NotNil(m, struct {
+		hello string
+	}{})
+	m.Expect(t, "")
+
+	NotNil(m, (*testing.T)(nil))
+	m.Expect(t, `assertion failed, unexpected: <nil>, expected: not nil`)
+
+}
+
+func TestDeepEqual(t *testing.T) {
+	m := &mockTestingT{}
+
+	DeepEqual(m, 1, 1)
+	m.Expect(t, "")
+
+	DeepEqual(m, 1, 2)
+	m.Expect(t, `assertion failed, unexpected: 2, expected: 1`)
+
+}
+
+func TestNotEqual(t *testing.T) {
+	m := &mockTestingT{}
+
+	NotEqual(m, 1, 2)
+	m.Expect(t, "")
+
+	NotEqual(m, nil, nil)
+	m.Expect(t, `assertion failed: <nil> == <nil>`)
+}
+
+func TestTrueFalse(t *testing.T) {
+	m := &mockTestingT{}
+
+	True(m, true)
+	m.Expect(t, "")
+
+	False(m, false)
+	m.Expect(t, "")
+}
+
+func TestPanic(t *testing.T) {
+	m := &mockTestingT{}
+
+	Panic(m, func() {
+		panic("hello")
+	})
+	m.Expect(t, "")
+	Panic(m, func() {
+	})
+	m.Expect(t, `assertion failed: did not panic`)
+
+	NotPanic(m, func() {
+	})
+	m.Expect(t, "")
+
+	NotPanic(m, func() {
+		panic("hello")
+	})
+	m.Expect(t, `assertion failed: panicked`)
+
+}