Repository: go-kratos/kratos
Pull request #2165 (2022-07-07 19:51:45)

tests(coverage): Increase middleware tests coverage
<!--
🎉 Thanks for sending a pull request to Kratos! Here are some tips for you:

1. If this is your first time contributing to Kratos, please read our contribution guide: https://go-kratos.dev/en/docs/community/contribution/
2. Ensure you have added or ran the appropriate tests and lint for your PR, please use `make lint` and `make test` before filing your PR, use `make clean` to tidy your go mod.
3. If the PR is unfinished, you may need to mark it as a WIP(Work In Progress) PR or draft PR
4. Please use a semantic commits format title, such as `<type>[optional scope]: <description>`, see: https://go-kratos.dev/docs/community/contribution#type
5. at the same time, please note that similar work should be submitted in one PR as far as possible to reduce the workload of reviewers. Do not split a work into multiple PR unless it should.
-->

<!--
🎉 感谢您向 Kratos 发送 PR 请求！以下是一些提示：
如果这是你第一次为 Kratos 贡献，请阅读我们的贡献指南：https://go-kratos.dev/en/docs/community/contribution/
2、确保您已经为您的 PR 添加或运行了适当的测试和lint，请在提交PR之前使用“make lint”和“make test”，使用“make clean”整理您的 go.mod。
3、如果请购单未完成，您可能需要将其标记为 WIP（在制品）PR 或 draft PR
4、请使用语义提交格式标题，如“<类型>[可选范围]：<说明>`，请参阅：https://go-kratos.dev/docs/community/contribution#type
5. 同时请注意，同类的工作请尽量在一个PR中提交，以减轻 review 者的工作负担，不要把一项工作拆分成很多个PR，除非它应该这样做。
-->

<!--
-->

#### Description (what this PR does / why we need it):
<!--
* The description should include the motivation for this PR or contrast this with previous behavior
-->
```
github.com/go-kratos/kratos/v2/middleware/auth/jwt/jwt.go:56:                   WithSigningMethod       100.0%
github.com/go-kratos/kratos/v2/middleware/auth/jwt/jwt.go:65:                   WithClaims              100.0%
github.com/go-kratos/kratos/v2/middleware/auth/jwt/jwt.go:72:                   WithTokenHeader         100.0%
github.com/go-kratos/kratos/v2/middleware/auth/jwt/jwt.go:79:                   Server                  87.5%
github.com/go-kratos/kratos/v2/middleware/auth/jwt/jwt.go:134:                  Client                  91.3%
github.com/go-kratos/kratos/v2/middleware/auth/jwt/jwt.go:172:                  NewContext              100.0%
github.com/go-kratos/kratos/v2/middleware/auth/jwt/jwt.go:177:                  FromContext             100.0%
github.com/go-kratos/kratos/v2/middleware/circuitbreaker/circuitbreaker.go:22:  WithGroup               100.0%
github.com/go-kratos/kratos/v2/middleware/circuitbreaker/circuitbreaker.go:34:  Client                  100.0%
github.com/go-kratos/kratos/v2/middleware/logging/logging.go:15:                Server                  100.0%
github.com/go-kratos/kratos/v2/middleware/logging/logging.go:51:                Client                  100.0%
github.com/go-kratos/kratos/v2/middleware/logging/logging.go:87:                extractArgs             100.0%
github.com/go-kratos/kratos/v2/middleware/logging/logging.go:95:                extractError            100.0%
github.com/go-kratos/kratos/v2/middleware/metadata/metadata.go:20:              hasPrefix               100.0%
github.com/go-kratos/kratos/v2/middleware/metadata/metadata.go:31:              WithConstants           100.0%
github.com/go-kratos/kratos/v2/middleware/metadata/metadata.go:38:              WithPropagatedPrefix    100.0%
github.com/go-kratos/kratos/v2/middleware/metadata/metadata.go:45:              Server                  100.0%
github.com/go-kratos/kratos/v2/middleware/metadata/metadata.go:70:              Client                  100.0%
github.com/go-kratos/kratos/v2/middleware/metrics/metrics.go:18:                WithRequests            100.0%
github.com/go-kratos/kratos/v2/middleware/metrics/metrics.go:25:                WithSeconds             100.0%
github.com/go-kratos/kratos/v2/middleware/metrics/metrics.go:39:                Server                  100.0%
github.com/go-kratos/kratos/v2/middleware/metrics/metrics.go:74:                Client                  100.0%
github.com/go-kratos/kratos/v2/middleware/middleware.go:14:                     Chain                   100.0%
github.com/go-kratos/kratos/v2/middleware/ratelimit/ratelimit.go:20:            WithLimiter             100.0%
github.com/go-kratos/kratos/v2/middleware/ratelimit/ratelimit.go:31:            Server                  100.0%
github.com/go-kratos/kratos/v2/middleware/recovery/recovery.go:26:              WithHandler             100.0%
github.com/go-kratos/kratos/v2/middleware/recovery/recovery.go:34:              WithLogger              100.0%
github.com/go-kratos/kratos/v2/middleware/recovery/recovery.go:39:              Recovery                100.0%
github.com/go-kratos/kratos/v2/middleware/selector/selector.go:41:              Server                  100.0%
github.com/go-kratos/kratos/v2/middleware/selector/selector.go:46:              Client                  100.0%
github.com/go-kratos/kratos/v2/middleware/selector/selector.go:51:              Prefix                  100.0%
github.com/go-kratos/kratos/v2/middleware/selector/selector.go:57:              Regex                   100.0%
github.com/go-kratos/kratos/v2/middleware/selector/selector.go:63:              Path                    100.0%
github.com/go-kratos/kratos/v2/middleware/selector/selector.go:69:              Match                   100.0%
github.com/go-kratos/kratos/v2/middleware/selector/selector.go:75:              Build                   100.0%
github.com/go-kratos/kratos/v2/middleware/selector/selector.go:86:              matches                 100.0%
github.com/go-kratos/kratos/v2/middleware/selector/selector.go:119:             selector                100.0%
github.com/go-kratos/kratos/v2/middleware/selector/selector.go:130:             pathMatch               100.0%
github.com/go-kratos/kratos/v2/middleware/selector/selector.go:134:             prefixMatch             100.0%
github.com/go-kratos/kratos/v2/middleware/selector/selector.go:138:             regexMatch              100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/metadata.go:19:               Inject                  100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/metadata.go:27:               Extract                 100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/metadata.go:43:               Fields                  100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/span.go:20:                   setClientSpan           100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/span.go:57:                   setServerSpan           100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/span.go:100:                  parseFullMethod         100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/span.go:119:                  peerAttr                100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/span.go:135:                  parseTarget             100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/statsHandler.go:16:           HandleConn              100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/statsHandler.go:21:           TagConn                 100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/statsHandler.go:26:           HandleRPC               100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/statsHandler.go:41:           TagRPC                  100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/tracer.go:24:                 NewTracer               100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/tracer.go:46:                 Start                   100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/tracer.go:61:                 End                     100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/tracing.go:23:                WithPropagator          100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/tracing.go:31:                WithTracerProvider      100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/tracing.go:38:                Server                  100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/tracing.go:54:                Client                  100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/tracing.go:70:                TraceID                 100.0%
github.com/go-kratos/kratos/v2/middleware/tracing/tracing.go:80:                SpanID                  100.0%
github.com/go-kratos/kratos/v2/middleware/validate/validate.go:15:              Validator               100.0%
total:                                                                          (statements)            98.6%
```
I am not able to improve the jwt.go Client/Server coverage.

#### Which issue(s) this PR fixes (resolves / be part of):
<!--
* Automatically closes linked issue when PR is merged.
* If your PR is not fully resolved the issue, please use `part of #<issue number>` instead.

Usage: `fixes/resolves #<issue number>`, or `fixes/resolves (paste link of issue)`.
-->
Part of #2147 
Middlewares coverage improvements


#### Other special notes for the reviewers:
<!--
* Somethings that need extra attention for the reviewers
* Some additional notes, TODO list, etc.
-->


haiyux: Please fix lint error, use `make fix`.

Patch:
diff --git a/contrib/config/consul/go.mod b/contrib/config/consul/go.mod
index 48fe75ed..6a0d984a 100644
--- a/contrib/config/consul/go.mod
+++ b/contrib/config/consul/go.mod
@@ -4,7 +4,7 @@ go 1.15
 
 require (
 	github.com/go-kratos/kratos/v2 v2.3.1
-	github.com/hashicorp/consul/api v1.12.0
+	github.com/hashicorp/consul/api v1.13.0
 )
 
 replace github.com/go-kratos/kratos/v2 => ../../../
diff --git a/contrib/config/consul/go.sum b/contrib/config/consul/go.sum
index 69f5d5bd..758fdcb1 100644
--- a/contrib/config/consul/go.sum
+++ b/contrib/config/consul/go.sum
@@ -70,8 +70,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/hashicorp/consul/api v1.12.0 h1:k3y1FYv6nuKyNTqj6w9gXOx5r5CfLj/k/euUeBXj1OY=
-github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=
+github.com/hashicorp/consul/api v1.13.0 h1:2hnLQ0GjQvw7f3O61jMO8gbasZviZTrt9R8WzgiirHc=
+github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ=
 github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU=
 github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
 github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
diff --git a/contrib/config/kubernetes/go.mod b/contrib/config/kubernetes/go.mod
index db846f9c..c4c0d82d 100644
--- a/contrib/config/kubernetes/go.mod
+++ b/contrib/config/kubernetes/go.mod
@@ -4,9 +4,9 @@ go 1.16
 
 require (
 	github.com/go-kratos/kratos/v2 v2.3.1
-	k8s.io/api v0.24.1
-	k8s.io/apimachinery v0.24.1
-	k8s.io/client-go v0.24.1
+	k8s.io/api v0.24.2
+	k8s.io/apimachinery v0.24.2
+	k8s.io/client-go v0.24.2
 )
 
 replace github.com/go-kratos/kratos/v2 => ../../../
diff --git a/contrib/config/kubernetes/go.sum b/contrib/config/kubernetes/go.sum
index ad234674..138314b9 100644
--- a/contrib/config/kubernetes/go.sum
+++ b/contrib/config/kubernetes/go.sum
@@ -652,12 +652,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-k8s.io/api v0.24.1 h1:BjCMRDcyEYz03joa3K1+rbshwh1Ay6oB53+iUx2H8UY=
-k8s.io/api v0.24.1/go.mod h1:JhoOvNiLXKTPQ60zh2g0ewpA+bnEYf5q44Flhquh4vQ=
-k8s.io/apimachinery v0.24.1 h1:ShD4aDxTQKN5zNf8K1RQ2u98ELLdIW7jEnlO9uAMX/I=
-k8s.io/apimachinery v0.24.1/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
-k8s.io/client-go v0.24.1 h1:w1hNdI9PFrzu3OlovVeTnf4oHDt+FJLd9Ndluvnb42E=
-k8s.io/client-go v0.24.1/go.mod h1:f1kIDqcEYmwXS/vTbbhopMUbhKp2JhOeVTfxgaCIlF8=
+k8s.io/api v0.24.2 h1:g518dPU/L7VRLxWfcadQn2OnsiGWVOadTLpdnqgY2OI=
+k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg=
+k8s.io/apimachinery v0.24.2 h1:5QlH9SL2C8KMcrNJPor+LbXVTaZRReml7svPEh4OKDM=
+k8s.io/apimachinery v0.24.2/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
+k8s.io/client-go v0.24.2 h1:CoXFSf8if+bLEbinDqN9ePIDGzcLtqhfd6jpfnwGOFA=
+k8s.io/client-go v0.24.2/go.mod h1:zg4Xaoo+umDsfCWr4fCnmLEtQXyCNXCvJuSsglNcV30=
 k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
 k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
 k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
diff --git a/contrib/registry/consul/go.mod b/contrib/registry/consul/go.mod
index f746247d..38f0f1aa 100644
--- a/contrib/registry/consul/go.mod
+++ b/contrib/registry/consul/go.mod
@@ -4,7 +4,7 @@ go 1.16
 
 require (
 	github.com/go-kratos/kratos/v2 v2.3.1
-	github.com/hashicorp/consul/api v1.12.0
+	github.com/hashicorp/consul/api v1.13.0
 )
 
 replace github.com/go-kratos/kratos/v2 => ../../../
diff --git a/contrib/registry/consul/go.sum b/contrib/registry/consul/go.sum
index 402ade80..b2e006cb 100644
--- a/contrib/registry/consul/go.sum
+++ b/contrib/registry/consul/go.sum
@@ -64,13 +64,14 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
 github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/hashicorp/consul/api v1.12.0 h1:k3y1FYv6nuKyNTqj6w9gXOx5r5CfLj/k/euUeBXj1OY=
-github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=
+github.com/hashicorp/consul/api v1.13.0 h1:2hnLQ0GjQvw7f3O61jMO8gbasZviZTrt9R8WzgiirHc=
+github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ=
 github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU=
 github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
 github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
@@ -222,6 +223,7 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
 golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
diff --git a/contrib/registry/kubernetes/go.sum b/contrib/registry/kubernetes/go.sum
index 199d8157..559f0670 100644
--- a/contrib/registry/kubernetes/go.sum
+++ b/contrib/registry/kubernetes/go.sum
@@ -201,6 +201,7 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
 github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
 github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
diff --git a/middleware/tracing/statsHandler.go b/middleware/tracing/statsHandler.go
index 11b12679..b9ee3fd1 100644
--- a/middleware/tracing/statsHandler.go
+++ b/middleware/tracing/statsHandler.go
@@ -2,6 +2,7 @@ package tracing
 
 import (
 	"context"
+	"fmt"
 
 	"go.opentelemetry.io/otel/trace"
 	"google.golang.org/grpc/peer"
@@ -13,6 +14,7 @@ type ClientHandler struct{}
 
 // HandleConn exists to satisfy gRPC stats.Handler.
 func (c *ClientHandler) HandleConn(ctx context.Context, cs stats.ConnStats) {
+	fmt.Println("Handle connection.")
 }
 
 // TagConn exists to satisfy gRPC stats.Handler.


Test patch:
diff --git a/config/file/format_test.go b/config/file/format_test.go
new file mode 100644
index 00000000..509a9837
--- /dev/null
+++ b/config/file/format_test.go
@@ -0,0 +1,41 @@
+package file
+
+import "testing"
+
+func TestFormat(t *testing.T) {
+	tests := []struct {
+		input  string
+		expect string
+	}{
+		{
+			input:  "",
+			expect: "",
+		},
+		{
+			input:  " ",
+			expect: "",
+		},
+		{
+			input:  ".",
+			expect: "",
+		},
+		{
+			input:  "a.",
+			expect: "",
+		},
+		{
+			input:  ".b",
+			expect: "b",
+		},
+		{
+			input:  "a.b",
+			expect: "b",
+		},
+	}
+	for _, v := range tests {
+		content := format(v.input)
+		if got, want := content, v.expect; got != want {
+			t.Errorf("expect %v,got %v", want, got)
+		}
+	}
+}
diff --git a/contrib/registry/kubernetes/registry_test.go b/contrib/registry/kubernetes/registry_test.go
new file mode 100644
index 00000000..2363b1c3
--- /dev/null
+++ b/contrib/registry/kubernetes/registry_test.go
@@ -0,0 +1,186 @@
+package kuberegistry
+
+import (
+	"context"
+	"os"
+	"path/filepath"
+	"testing"
+	"time"
+
+	"github.com/go-kratos/kratos/v2/registry"
+
+	appsv1 "k8s.io/api/apps/v1"
+	apiv1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/client-go/kubernetes"
+	"k8s.io/client-go/rest"
+	"k8s.io/client-go/tools/clientcmd"
+	"k8s.io/client-go/util/homedir"
+)
+
+const (
+	namespace  = "default"
+	deployName = "hello-deployment"
+	podName    = "hello"
+)
+
+var deployment = appsv1.Deployment{
+	ObjectMeta: metav1.ObjectMeta{
+		Name: deployName,
+	},
+	Spec: appsv1.DeploymentSpec{
+		Replicas: int32Ptr(1),
+		Selector: &metav1.LabelSelector{
+			MatchLabels: map[string]string{
+				"app": podName,
+			},
+		},
+		Template: apiv1.PodTemplateSpec{
+			ObjectMeta: metav1.ObjectMeta{
+				Labels: map[string]string{
+					"app": podName,
+				},
+			},
+			Spec: apiv1.PodSpec{
+				Containers: []apiv1.Container{
+					{
+						Name:  "nginx",
+						Image: "nginx:alpine",
+						Ports: []apiv1.ContainerPort{
+							{
+								Name:          "http",
+								Protocol:      apiv1.ProtocolTCP,
+								ContainerPort: 80,
+							},
+						},
+						Command: []string{
+							"nginx",
+							"-g",
+							"daemon off;",
+						},
+					},
+				},
+			},
+		},
+	},
+}
+
+func getClientSet() (*kubernetes.Clientset, error) {
+	restConfig, err := rest.InClusterConfig()
+	home := homedir.HomeDir()
+
+	if err != nil {
+		kubeconfig := filepath.Join(home, ".kube", "config")
+		restConfig, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
+		if err != nil {
+			return nil, err
+		}
+	}
+	clientSet, err := kubernetes.NewForConfig(restConfig)
+	if err != nil {
+		return nil, err
+	}
+	return clientSet, nil
+}
+
+func int32Ptr(i int32) *int32 { return &i }
+
+func TestSetEnv(t *testing.T) {
+	os.Setenv("HOSTNAME", podName)
+	if os.Getenv("HOSTNAME") != podName {
+		t.Fatal("error")
+	}
+}
+
+func TestRegistry(t *testing.T) {
+	currentNamespace = "default"
+
+	clientSet, err := getClientSet()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	r := NewRegistry(clientSet)
+	r.Start()
+
+	svrHello := &registry.ServiceInstance{
+		ID:        "1",
+		Name:      "hello",
+		Version:   "v1.0.0",
+		Endpoints: []string{"http://127.0.0.1:80"},
+	}
+	_, err = clientSet.AppsV1().Deployments(namespace).Create(context.Background(), &deployment, metav1.CreateOptions{})
+
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	watch, err := r.Watch(context.Background(), svrHello.Name)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	defer func() {
+		_ = watch.Stop()
+	}()
+
+	go func() {
+		for {
+			res, err1 := watch.Next()
+			if err1 != nil {
+				return
+			}
+			t.Logf("watch: %d", len(res))
+			for _, r := range res {
+				t.Logf("next: %+v", r)
+			}
+		}
+	}()
+	time.Sleep(time.Second)
+
+	pod, err := clientSet.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{
+		LabelSelector: "app=hello",
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(pod.Items) < 1 {
+		t.Fatal("fetch resource error")
+	}
+
+	os.Setenv("HOSTNAME", pod.Items[0].Name)
+
+	// Alway remember delete test resource
+	defer func() {
+		_ = clientSet.AppsV1().Deployments(namespace).Delete(context.Background(), deployName, metav1.DeleteOptions{})
+	}()
+
+	if err = r.Register(context.Background(), svrHello); err != nil {
+		t.Fatal(err)
+	}
+	time.Sleep(time.Second)
+
+	res, err := r.GetService(context.Background(), svrHello.Name)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(res) != 1 && res[0].Name != svrHello.Name {
+		t.Fatal(err)
+	}
+
+	if err1 := r.Deregister(context.Background(), svrHello); err1 != nil {
+		t.Fatal(err1)
+	}
+
+	time.Sleep(time.Second)
+
+	res, err = r.GetService(context.Background(), svrHello.Name)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(res) != 0 {
+		t.Fatal("not expected empty")
+	}
+}
diff --git a/middleware/circuitbreaker/circuitbreaker_test.go b/middleware/circuitbreaker/circuitbreaker_test.go
new file mode 100644
index 00000000..b7c2553c
--- /dev/null
+++ b/middleware/circuitbreaker/circuitbreaker_test.go
@@ -0,0 +1,79 @@
+package circuitbreaker
+
+import (
+	"context"
+	"errors"
+	"testing"
+
+	kratos_errors "github.com/go-kratos/kratos/v2/errors"
+	"github.com/go-kratos/kratos/v2/internal/group"
+	"github.com/go-kratos/kratos/v2/transport"
+)
+
+type transportMock struct {
+	kind      transport.Kind
+	endpoint  string
+	operation string
+}
+
+type circuitBreakerMock struct {
+	err error
+}
+
+func (tr *transportMock) Kind() transport.Kind {
+	return tr.kind
+}
+
+func (tr *transportMock) Endpoint() string {
+	return tr.endpoint
+}
+
+func (tr *transportMock) Operation() string {
+	return tr.operation
+}
+
+func (tr *transportMock) RequestHeader() transport.Header {
+	return nil
+}
+
+func (tr *transportMock) ReplyHeader() transport.Header {
+	return nil
+}
+
+func (c *circuitBreakerMock) Allow() error { return c.err }
+func (c *circuitBreakerMock) MarkSuccess() {}
+func (c *circuitBreakerMock) MarkFailed()  {}
+
+func Test_WithGroup(t *testing.T) {
+	o := options{
+		group: group.NewGroup(func() interface{} {
+			return ""
+		}),
+	}
+
+	WithGroup(nil)(&o)
+	if o.group != nil {
+		t.Error("The group property must be updated to nil.")
+	}
+}
+
+func Test_Server(t *testing.T) {
+	nextValid := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return "Hello valid", nil
+	}
+	nextInvalid := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return nil, kratos_errors.InternalServer("", "")
+	}
+
+	ctx := transport.NewClientContext(context.Background(), &transportMock{})
+
+	_, _ = Client(func(o *options) {
+		o.group = group.NewGroup(func() interface{} {
+			return &circuitBreakerMock{err: errors.New("circuitbreaker error")}
+		})
+	})(nextValid)(ctx, nil)
+
+	_, _ = Client(func(_ *options) {})(nextValid)(ctx, nil)
+
+	_, _ = Client(func(_ *options) {})(nextInvalid)(ctx, nil)
+}
diff --git a/middleware/logging/logging_test.go b/middleware/logging/logging_test.go
index 0427d11e..7242f8e0 100644
--- a/middleware/logging/logging_test.go
+++ b/middleware/logging/logging_test.go
@@ -97,3 +97,26 @@ func TestHTTP(t *testing.T) {
 		})
 	}
 }
+
+type (
+	dummy struct {
+		field string
+	}
+	dummyStringer struct {
+		field string
+	}
+)
+
+func (d *dummyStringer) String() string {
+	return "my value"
+}
+
+func Test_extractArgs(t *testing.T) {
+	if extractArgs(&dummyStringer{field: ""}) != "my value" {
+		t.Errorf(`The stringified dummyStringer structure must be equal to "my value", %v given`, extractArgs(&dummyStringer{field: ""}))
+	}
+
+	if extractArgs(&dummy{field: "value"}) != "&{field:value}" {
+		t.Errorf(`The stringified dummy structure must be equal to "&{field:value}", %v given`, extractArgs(&dummy{field: "value"}))
+	}
+}
diff --git a/middleware/metadata/metadata_test.go b/middleware/metadata/metadata_test.go
index c286eedb..d0190b67 100644
--- a/middleware/metadata/metadata_test.go
+++ b/middleware/metadata/metadata_test.go
@@ -126,3 +126,14 @@ func TestClient(t *testing.T) {
 		t.Fatalf("want foo got %v", reply)
 	}
 }
+
+func Test_WithPropagatedPrefix(t *testing.T) {
+	o := &options{
+		prefix: []string{"override"},
+	}
+	WithPropagatedPrefix("something", "another")(o)
+
+	if len(o.prefix) != 2 {
+		t.Error("The prefix must be overrided.")
+	}
+}
diff --git a/middleware/metrics/metrics_test.go b/middleware/metrics/metrics_test.go
index c695f275..9cf261ce 100644
--- a/middleware/metrics/metrics_test.go
+++ b/middleware/metrics/metrics_test.go
@@ -2,20 +2,155 @@ package metrics
 
 import (
 	"context"
+	"errors"
 	"testing"
+
+	"github.com/go-kratos/kratos/v2/metrics"
+	"github.com/go-kratos/kratos/v2/transport"
+	"github.com/go-kratos/kratos/v2/transport/http"
 )
 
-func TestMetrics(t *testing.T) {
-	next := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return req.(string) + "https://go-kratos.dev", nil
+type (
+	mockCounter struct {
+		lvs   []string
+		value float64
+	}
+	mockObserver struct {
+		lvs   []string
+		value float64
 	}
-	_, err := Server()(next)(context.Background(), "test:")
+)
+
+func (m *mockCounter) With(lvs ...string) metrics.Counter {
+	return m
+}
+
+func (m *mockCounter) Inc() {
+	m.value += 1.0
+}
+
+func (m *mockCounter) Add(delta float64) {
+	m.value += delta
+}
+
+func (m *mockObserver) With(lvs ...string) metrics.Observer {
+	return m
+}
+
+func (m *mockObserver) Observe(delta float64) {
+	m.value += delta
+}
+
+func TestWithRequests(t *testing.T) {
+	mock := mockCounter{
+		lvs:   []string{"Initial"},
+		value: 1.23,
+	}
+	o := options{
+		requests: &mock,
+	}
+
+	WithRequests(&mock)(&o)
+
+	if _, ok := o.requests.(*mockCounter); !ok {
+		t.Errorf(`The type of the option requests property must be of "mockCounter", %T given.`, o.requests)
+	}
+
+	counter := o.requests.(*mockCounter)
+
+	if len(counter.lvs) != 1 || counter.lvs[0] != "Initial" {
+		t.Errorf(`The given counter lvs must have only one element equal to "Initial", %v given`, counter.lvs)
+	}
+	if counter.value != 1.23 {
+		t.Errorf(`The given counter value must be equal to 1.23, %v given`, counter.value)
+	}
+}
+
+func TestWithSeconds(t *testing.T) {
+	mock := mockObserver{
+		lvs:   []string{"Initial"},
+		value: 1.23,
+	}
+	o := options{
+		seconds: &mock,
+	}
+
+	WithSeconds(&mock)(&o)
+
+	if _, ok := o.seconds.(*mockObserver); !ok {
+		t.Errorf(`The type of the option requests property must be of "mockObserver", %T given.`, o.requests)
+	}
+
+	observer := o.seconds.(*mockObserver)
+
+	if len(observer.lvs) != 1 || observer.lvs[0] != "Initial" {
+		t.Errorf(`The given observer lvs must have only one element equal to "Initial", %v given`, observer.lvs)
+	}
+	if observer.value != 1.23 {
+		t.Errorf(`The given observer value must be equal to 1.23, %v given`, observer.value)
+	}
+}
+
+func TestServer(t *testing.T) {
+	e := errors.New("got an error")
+	nextError := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return nil, e
+	}
+	nextValid := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return "Hello valid", nil
+	}
+
+	_, err := Server()(nextError)(context.Background(), "test:")
+	if err != e {
+		t.Error("The given error mismatch the expected.")
+	}
+
+	res, err := Server(func(o *options) {
+		o.requests = &mockCounter{
+			lvs:   []string{"Initial"},
+			value: 1.23,
+		}
+		o.seconds = &mockObserver{
+			lvs:   []string{"Initial"},
+			value: 1.23,
+		}
+	})(nextValid)(transport.NewServerContext(context.Background(), &http.Transport{}), "test:")
 	if err != nil {
-		t.Errorf("expect %v, got %v", nil, err)
+		t.Error("The server must not throw an error.")
+	}
+	if res != "Hello valid" {
+		t.Error(`The server must return a "Hello valid" response.`)
+	}
+}
+
+func TestClient(t *testing.T) {
+	e := errors.New("got an error")
+	nextError := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return nil, e
+	}
+	nextValid := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return "Hello valid", nil
 	}
 
-	_, err = Client()(next)(context.Background(), "test:")
+	_, err := Client()(nextError)(context.Background(), "test:")
+	if err != e {
+		t.Error("The given error mismatch the expected.")
+	}
+
+	res, err := Client(func(o *options) {
+		o.requests = &mockCounter{
+			lvs:   []string{"Initial"},
+			value: 1.23,
+		}
+		o.seconds = &mockObserver{
+			lvs:   []string{"Initial"},
+			value: 1.23,
+		}
+	})(nextValid)(transport.NewClientContext(context.Background(), &http.Transport{}), "test:")
 	if err != nil {
-		t.Errorf("expect %v, got %v", nil, err)
+		t.Error("The server must not throw an error.")
+	}
+	if res != "Hello valid" {
+		t.Error(`The server must return a "Hello valid" response.`)
 	}
 }
diff --git a/middleware/ratelimit/ratelimit_test.go b/middleware/ratelimit/ratelimit_test.go
new file mode 100644
index 00000000..2e4e5bde
--- /dev/null
+++ b/middleware/ratelimit/ratelimit_test.go
@@ -0,0 +1,64 @@
+package ratelimit
+
+import (
+	"context"
+	"errors"
+	"testing"
+
+	"github.com/go-kratos/aegis/ratelimit"
+)
+
+type (
+	ratelimitMock struct {
+		reached bool
+	}
+	ratelimitReachedMock struct {
+		reached bool
+	}
+)
+
+func (r *ratelimitMock) Allow() (ratelimit.DoneFunc, error) {
+	return func(_ ratelimit.DoneInfo) {
+		r.reached = true
+	}, nil
+}
+
+func (r *ratelimitReachedMock) Allow() (ratelimit.DoneFunc, error) {
+	return func(_ ratelimit.DoneInfo) {
+		r.reached = true
+	}, errors.New("errored")
+}
+
+func Test_WithLimiter(t *testing.T) {
+	o := options{
+		limiter: &ratelimitMock{},
+	}
+
+	WithLimiter(nil)(&o)
+	if o.limiter != nil {
+		t.Error("The limiter property must be updated.")
+	}
+}
+
+func Test_Server(t *testing.T) {
+	nextValid := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return "Hello valid", nil
+	}
+
+	rlm := &ratelimitMock{}
+	rlrm := &ratelimitReachedMock{}
+
+	_, _ = Server(func(o *options) {
+		o.limiter = rlm
+	})(nextValid)(context.Background(), nil)
+	if !rlm.reached {
+		t.Error("The ratelimit must run the done function.")
+	}
+
+	_, _ = Server(func(o *options) {
+		o.limiter = rlrm
+	})(nextValid)(context.Background(), nil)
+	if rlrm.reached {
+		t.Error("The ratelimit must not run the done function and should be denied.")
+	}
+}
diff --git a/middleware/recovery/recovery_test.go b/middleware/recovery/recovery_test.go
index cf4a7fbb..985cb19b 100644
--- a/middleware/recovery/recovery_test.go
+++ b/middleware/recovery/recovery_test.go
@@ -6,6 +6,7 @@ import (
 	"testing"
 
 	"github.com/go-kratos/kratos/v2/errors"
+	"github.com/go-kratos/kratos/v2/log"
 )
 
 func TestOnce(t *testing.T) {
@@ -34,3 +35,8 @@ func TestNotPanic(t *testing.T) {
 		t.Errorf("e isn't nil")
 	}
 }
+
+// Deprecated: Remove this test with WithLogger method.
+func TestWithLogger(t *testing.T) {
+	_ = WithLogger(log.DefaultLogger)
+}
diff --git a/middleware/selector/selector_test.go b/middleware/selector/selector_test.go
index afde9530..667d2755 100644
--- a/middleware/selector/selector_test.go
+++ b/middleware/selector/selector_test.go
@@ -2,7 +2,6 @@ package selector
 
 import (
 	"context"
-	"fmt"
 	"reflect"
 	"strings"
 	"testing"
@@ -243,9 +242,20 @@ func TestHeaderFunc(t *testing.T) {
 
 func testMiddleware(handler middleware.Handler) middleware.Handler {
 	return func(ctx context.Context, req interface{}) (reply interface{}, err error) {
-		fmt.Println("before")
 		reply, err = handler(ctx, req)
-		fmt.Println("after")
 		return
 	}
 }
+
+func Test_RegexMatch(t *testing.T) {
+	if regexMatch("^\b(?", "something") {
+		t.Error("The invalid regex must not match.")
+	}
+}
+
+func Test_matches(t *testing.T) {
+	b := Builder{}
+	if b.matches(context.Background(), func(_ context.Context) (transport.Transporter, bool) { return nil, false }) {
+		t.Error("The matches method must return false.")
+	}
+}
diff --git a/middleware/tracing/metadata_test.go b/middleware/tracing/metadata_test.go
index a4f45ed6..09446c2a 100644
--- a/middleware/tracing/metadata_test.go
+++ b/middleware/tracing/metadata_test.go
@@ -51,9 +51,10 @@ func TestMetadata_Extract(t *testing.T) {
 		carrier propagation.TextMapCarrier
 	}
 	tests := []struct {
-		name string
-		args args
-		want string
+		name  string
+		args  args
+		want  string
+		crash bool
 	}{
 		{
 			name: "https://go-kratos.dev",
@@ -71,6 +72,14 @@ func TestMetadata_Extract(t *testing.T) {
 			},
 			want: "https://github.com/go-kratos/kratos",
 		},
+		{
+			name: "https://github.com/go-kratos/kratos",
+			args: args{
+				parent:  metadata.NewServerContext(context.Background(), metadata.Metadata{}),
+				carrier: propagation.HeaderCarrier{"X-Md-Service-Name": nil},
+			},
+			crash: true,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -78,6 +87,9 @@ func TestMetadata_Extract(t *testing.T) {
 			ctx := b.Extract(tt.args.parent, tt.args.carrier)
 			md, ok := metadata.FromServerContext(ctx)
 			if !ok {
+				if tt.crash {
+					return
+				}
 				t.Errorf("expect %v, got %v", true, ok)
 			}
 			if !reflect.DeepEqual(md.Get(serviceHeader), tt.want) {
diff --git a/middleware/tracing/span_test.go b/middleware/tracing/span_test.go
index c930154f..aad4614f 100644
--- a/middleware/tracing/span_test.go
+++ b/middleware/tracing/span_test.go
@@ -1,11 +1,19 @@
 package tracing
 
 import (
+	"context"
+	"net"
+	"net/http"
 	"reflect"
 	"testing"
 
+	"github.com/go-kratos/kratos/v2/internal/testdata/binding"
+	"github.com/go-kratos/kratos/v2/metadata"
+	"github.com/go-kratos/kratos/v2/transport"
 	"go.opentelemetry.io/otel/attribute"
 	semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
+	"go.opentelemetry.io/otel/trace"
+	"google.golang.org/grpc/peer"
 )
 
 func Test_parseFullMethod(t *testing.T) {
@@ -142,6 +150,18 @@ func Test_parseTarget(t *testing.T) {
 			wantAddress: "hello",
 			wantErr:     false,
 		},
+		{
+			name:        "empty",
+			endpoint:    "%%",
+			wantAddress: "",
+			wantErr:     true,
+		},
+		{
+			name:        "invalid path",
+			endpoint:    "//%2F/#%2Fanother",
+			wantAddress: "",
+			wantErr:     false,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -156,3 +176,74 @@ func Test_parseTarget(t *testing.T) {
 		})
 	}
 }
+
+func Test_setServerSpan(t *testing.T) {
+	ctx := context.Background()
+	_, span := trace.NewNoopTracerProvider().Tracer("Tracer").Start(ctx, "Spanname")
+
+	// Handle without Transport context
+	setServerSpan(ctx, span, nil)
+
+	// Handle with proto message
+	m := &binding.HelloRequest{}
+	setServerSpan(ctx, span, m)
+
+	// Handle with metadata context
+	ctx = metadata.NewServerContext(ctx, metadata.New())
+	setServerSpan(ctx, span, m)
+
+	// Handle with KindHTTP transport context
+	mt := &mockTransport{
+		kind: transport.KindHTTP,
+	}
+	mt.request, _ = http.NewRequest(http.MethodGet, "/endpoint", nil)
+	ctx = transport.NewServerContext(ctx, mt)
+	setServerSpan(ctx, span, m)
+
+	// Handle with KindGRPC transport context
+	mt.kind = transport.KindGRPC
+	ctx = transport.NewServerContext(ctx, mt)
+	ip, _ := net.ResolveIPAddr("ip", "1.1.1.1")
+	ctx = peer.NewContext(ctx, &peer.Peer{
+		Addr: ip,
+	})
+	setServerSpan(ctx, span, m)
+}
+
+func Test_setClientSpan(t *testing.T) {
+	ctx := context.Background()
+	_, span := trace.NewNoopTracerProvider().Tracer("Tracer").Start(ctx, "Spanname")
+
+	// Handle without Transport context
+	setClientSpan(ctx, span, nil)
+
+	// Handle with proto message
+	m := &binding.HelloRequest{}
+	setClientSpan(ctx, span, m)
+
+	// Handle with metadata context
+	ctx = metadata.NewClientContext(ctx, metadata.New())
+	setClientSpan(ctx, span, m)
+
+	// Handle with KindHTTP transport context
+	mt := &mockTransport{
+		kind: transport.KindHTTP,
+	}
+	mt.request, _ = http.NewRequest(http.MethodGet, "/endpoint", nil)
+	mt.request.Host = "MyServer"
+	ctx = transport.NewClientContext(ctx, mt)
+	setClientSpan(ctx, span, m)
+
+	// Handle with KindGRPC transport context
+	mt.kind = transport.KindGRPC
+	ctx = transport.NewClientContext(ctx, mt)
+	ip, _ := net.ResolveIPAddr("ip", "1.1.1.1")
+	ctx = peer.NewContext(ctx, &peer.Peer{
+		Addr: ip,
+	})
+	setClientSpan(ctx, span, m)
+
+	// Handle without Host request
+	ctx = transport.NewClientContext(ctx, mt)
+	setClientSpan(ctx, span, m)
+}
diff --git a/middleware/tracing/statsHandler_test.go b/middleware/tracing/statsHandler_test.go
new file mode 100644
index 00000000..d8a38dcd
--- /dev/null
+++ b/middleware/tracing/statsHandler_test.go
@@ -0,0 +1,81 @@
+package tracing
+
+import (
+	"context"
+	"net"
+	"testing"
+
+	"go.opentelemetry.io/otel/trace"
+	"google.golang.org/grpc/peer"
+	"google.golang.org/grpc/stats"
+)
+
+type ctxKey string
+
+const testKey ctxKey = "MY_TEST_KEY"
+
+func Test_Client_HandleConn(t *testing.T) {
+	(&ClientHandler{}).HandleConn(context.Background(), nil)
+}
+
+func Test_Client_TagConn(t *testing.T) {
+	client := &ClientHandler{}
+	ctx := context.WithValue(context.Background(), testKey, 123)
+
+	if client.TagConn(ctx, nil).Value(testKey) != 123 {
+		t.Errorf(`The context value must be 123 for the "MY_KEY_TEST" key, %v given.`, client.TagConn(ctx, nil).Value(testKey))
+	}
+}
+
+func Test_Client_TagRPC(t *testing.T) {
+	client := &ClientHandler{}
+	ctx := context.WithValue(context.Background(), testKey, 123)
+
+	if client.TagRPC(ctx, nil).Value(testKey) != 123 {
+		t.Errorf(`The context value must be 123 for the "MY_KEY_TEST" key, %v given.`, client.TagConn(ctx, nil).Value(testKey))
+	}
+}
+
+type (
+	mockSpan struct {
+		trace.Span
+		mockSpanCtx *trace.SpanContext
+	}
+)
+
+func (m *mockSpan) SpanContext() trace.SpanContext {
+	return *m.mockSpanCtx
+}
+
+func Test_Client_HandleRPC(t *testing.T) {
+	client := &ClientHandler{}
+	ctx := context.Background()
+	rs := stats.OutHeader{}
+
+	// Handle stats.RPCStats is not type of stats.OutHeader case
+	client.HandleRPC(context.TODO(), nil)
+
+	// Handle context doesn't have the peerkey filled with a Peer instance
+	client.HandleRPC(ctx, &rs)
+
+	// Handle context with the peerkey filled with a Peer instance
+	ip, _ := net.ResolveIPAddr("ip", "1.1.1.1")
+	ctx = peer.NewContext(ctx, &peer.Peer{
+		Addr: ip,
+	})
+	client.HandleRPC(ctx, &rs)
+
+	// Handle context with Span
+	_, span := trace.NewNoopTracerProvider().Tracer("Tracer").Start(ctx, "Spanname")
+	spanCtx := trace.SpanContext{}
+	spanID := [8]byte{12, 12, 12, 12, 12, 12, 12, 12}
+	traceID := [16]byte{12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}
+	spanCtx = spanCtx.WithTraceID(traceID)
+	spanCtx = spanCtx.WithSpanID(spanID)
+	mSpan := mockSpan{
+		Span:        span,
+		mockSpanCtx: &spanCtx,
+	}
+	ctx = trace.ContextWithSpan(ctx, &mSpan)
+	client.HandleRPC(ctx, &rs)
+}
diff --git a/middleware/tracing/tracer_test.go b/middleware/tracing/tracer_test.go
new file mode 100644
index 00000000..fbd51c07
--- /dev/null
+++ b/middleware/tracing/tracer_test.go
@@ -0,0 +1,54 @@
+package tracing
+
+import (
+	"context"
+	"errors"
+	"testing"
+
+	"github.com/go-kratos/kratos/v2/internal/testdata/binding"
+	"go.opentelemetry.io/otel/trace"
+)
+
+func Test_NewTracer(t *testing.T) {
+	tracer := NewTracer(trace.SpanKindClient, func(o *options) {
+		o.tracerProvider = trace.NewNoopTracerProvider()
+	})
+
+	if tracer.kind != trace.SpanKindClient {
+		t.Errorf("The tracer kind must be equal to trace.SpanKindClient, %v given.", tracer.kind)
+	}
+
+	defer func() {
+		if recover() == nil {
+			t.Error("The NewTracer with an invalid SpanKindMustCrash must panic")
+		}
+	}()
+	_ = NewTracer(666, func(o *options) {
+		o.tracerProvider = trace.NewNoopTracerProvider()
+	})
+}
+
+func Test_Tracer_End(t *testing.T) {
+	tracer := NewTracer(trace.SpanKindClient, func(o *options) {
+		o.tracerProvider = trace.NewNoopTracerProvider()
+	})
+	ctx, span := trace.NewNoopTracerProvider().Tracer("noop").Start(context.Background(), "noopSpan")
+
+	// Handle with error case
+	tracer.End(ctx, span, nil, errors.New("dummy error"))
+
+	// Handle without error case
+	tracer.End(ctx, span, nil, nil)
+
+	m := &binding.HelloRequest{}
+
+	// Handle the trace KindServer
+	tracer = NewTracer(trace.SpanKindServer, func(o *options) {
+		o.tracerProvider = trace.NewNoopTracerProvider()
+	})
+	tracer.End(ctx, span, m, nil)
+	tracer = NewTracer(trace.SpanKindClient, func(o *options) {
+		o.tracerProvider = trace.NewNoopTracerProvider()
+	})
+	tracer.End(ctx, span, m, nil)
+}
diff --git a/middleware/tracing/tracing_test.go b/middleware/tracing/tracing_test.go
index d5fc0ed6..8de2ceea 100644
--- a/middleware/tracing/tracing_test.go
+++ b/middleware/tracing/tracing_test.go
@@ -42,6 +42,7 @@ type mockTransport struct {
 	endpoint  string
 	operation string
 	header    headerCarrier
+	request   *http.Request
 }
 
 func (tr *mockTransport) Kind() transport.Kind            { return tr.kind }
@@ -49,6 +50,16 @@ func (tr *mockTransport) Endpoint() string                { return tr.endpoint }
 func (tr *mockTransport) Operation() string               { return tr.operation }
 func (tr *mockTransport) RequestHeader() transport.Header { return tr.header }
 func (tr *mockTransport) ReplyHeader() transport.Header   { return tr.header }
+func (tr *mockTransport) Request() *http.Request {
+	if tr.request == nil {
+		rq, _ := http.NewRequest(http.MethodGet, "/endpoint", nil)
+
+		return rq
+	}
+
+	return tr.request
+}
+func (tr *mockTransport) PathTemplate() string { return "" }
 
 func TestTracer(t *testing.T) {
 	carrier := headerCarrier{}