Golang中如何进行JWT单元测试
Golang中如何进行JWT单元测试 我目前正在对我的项目进行单元测试,但对于如何对 jwt-go 包中的 JWT 进行单元测试感到困惑。我应该只使用一个测试用例来检查所有错误,还是应该将每个错误作为单独的测试用例来处理?
// 示例代码块,请根据实际测试代码替换
func TestJWTValidation(t *testing.T) {
// 测试逻辑将在此处编写
}
我应该只用一个测试用例来检查任何错误,还是将每个错误作为单独的测试用例来处理?
两种方式都是正确的。这归结为一个问题:你愿意做多少工作?
为任何错误使用单一测试用例简单且不那么痛苦。将每个错误作为单独的测试用例来处理可以让你获得总体上更好的覆盖率,但会引入更多的复杂性,并且对于简单的项目来说可能有些过度。
更多关于Golang中如何进行JWT单元测试的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
ScriptMang:
我目前正在对我的项目进行单元测试,但对于如何对 jwt-go 包中的 JWT 进行单元测试感到困惑。我是应该只用一个测试用例来检查所有错误,还是将每个错误作为单独的测试用例来处理?官方网站
每个测试用例理想情况下应该测试一个特定的方面。当你用一个测试来检查所有错误时,如果测试失败,就很难清楚到底是哪个具体的错误条件导致了失败。
对于JWT单元测试,建议将每个关键路径和错误场景作为独立的测试用例来处理。这样可以提高测试的可读性和维护性,并能更精确地定位问题。以下是基于jwt-go v4版本的单元测试示例:
import (
"testing"
"time"
"github.com/golang-jwt/jwt/v4"
)
// 测试有效令牌验证
func TestValidToken(t *testing.T) {
claims := &jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
Issuer: "test",
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
signedToken, err := token.SignedString([]byte("secret"))
if err != nil {
t.Fatalf("Failed to sign token: %v", err)
}
parsedToken, err := jwt.ParseWithClaims(signedToken, &jwt.RegisteredClaims{},
func(token *jwt.Token) (interface{}, error) {
return []byte("secret"), nil
})
if err != nil {
t.Errorf("Expected valid token, got error: %v", err)
}
if !parsedToken.Valid {
t.Error("Token should be valid")
}
}
// 测试过期令牌
func TestExpiredToken(t *testing.T) {
claims := &jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(-time.Hour)),
Issuer: "test",
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
signedToken, err := token.SignedString([]byte("secret"))
if err != nil {
t.Fatalf("Failed to sign token: %v", err)
}
_, err = jwt.ParseWithClaims(signedToken, &jwt.RegisteredClaims{},
func(token *jwt.Token) (interface{}, error) {
return []byte("secret"), nil
})
if err == nil {
t.Error("Expected token expiration error")
}
if !errors.Is(err, jwt.ErrTokenExpired) {
t.Errorf("Expected ErrTokenExpired, got: %v", err)
}
}
// 测试无效签名
func TestInvalidSignature(t *testing.T) {
claims := &jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
signedToken, err := token.SignedString([]byte("correct-secret"))
if err != nil {
t.Fatalf("Failed to sign token: %v", err)
}
_, err = jwt.ParseWithClaims(signedToken, &jwt.RegisteredClaims{},
func(token *jwt.Token) (interface{}, error) {
return []byte("wrong-secret"), nil
})
if err == nil {
t.Error("Expected signature validation error")
}
}
// 测试无效签名方法
func TestInvalidSigningMethod(t *testing.T) {
claims := &jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
signedToken, err := token.SignedString([]byte("secret"))
if err != nil {
t.Fatalf("Failed to sign token: %v", err)
}
_, err = jwt.ParseWithClaims(signedToken, &jwt.RegisteredClaims{},
func(token *jwt.Token) (interface{}, error) {
// 返回错误的密钥类型
return []byte("secret"), nil
})
if err == nil {
t.Error("Expected signing method error")
}
}
// 测试自定义验证逻辑
func TestCustomValidation(t *testing.T) {
type CustomClaims struct {
UserID string `json:"user_id"`
jwt.RegisteredClaims
}
claims := &CustomClaims{
UserID: "123",
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
signedToken, err := token.SignedString([]byte("secret"))
if err != nil {
t.Fatalf("Failed to sign token: %v", err)
}
parsedToken, err := jwt.ParseWithClaims(signedToken, &CustomClaims{},
func(token *jwt.Token) (interface{}, error) {
return []byte("secret"), nil
})
if err != nil {
t.Errorf("Failed to parse token: %v", err)
return
}
if customClaims, ok := parsedToken.Claims.(*CustomClaims); ok {
if customClaims.UserID != "123" {
t.Errorf("Expected user_id=123, got %s", customClaims.UserID)
}
} else {
t.Error("Failed to cast claims to CustomClaims")
}
}
使用表格驱动测试来组织相关测试用例:
func TestTokenValidationScenarios(t *testing.T) {
testCases := []struct {
name string
signingKey []byte
verifyKey []byte
claims jwt.Claims
expectError bool
errorType error
}{
{
name: "valid token",
signingKey: []byte("secret"),
verifyKey: []byte("secret"),
claims: &jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
},
expectError: false,
},
{
name: "wrong secret",
signingKey: []byte("secret"),
verifyKey: []byte("wrong"),
claims: &jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
},
expectError: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, tc.claims)
signedToken, err := token.SignedString(tc.signingKey)
if err != nil {
t.Fatalf("Failed to sign token: %v", err)
}
_, err = jwt.ParseWithClaims(signedToken, &jwt.RegisteredClaims{},
func(token *jwt.Token) (interface{}, error) {
return tc.verifyKey, nil
})
if tc.expectError && err == nil {
t.Error("Expected error but got none")
}
if !tc.expectError && err != nil {
t.Errorf("Expected no error but got: %v", err)
}
if tc.errorType != nil && !errors.Is(err, tc.errorType) {
t.Errorf("Expected error type %v, got %v", tc.errorType, err)
}
})
}
}
这种测试结构可以清晰地分离不同场景,便于维护和调试。每个测试用例都专注于特定的功能或错误条件,当测试失败时能快速定位问题所在。

