Files
bulma/error_main_test.go
T

155 lines
3.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"context"
"errors"
"fmt"
"strings"
"testing"
)
// ------------------------------
// 错误类型定义(核心优化)
// ------------------------------
// FileError 自定义文件服务错误结构体
type FileError struct {
Code int // 错误码
Module string // 所属模块
Message string // 错误信息
Err error // 原始错误(支持错误包装/解包)
}
// Error 实现 error 接口
func (e *FileError) Error() string {
if e.Err != nil {
return fmt.Sprintf("[%s:%d] %s: %v", e.Module, e.Code, e.Message, e.Err)
}
return fmt.Sprintf("[%s:%d] %s", e.Module, e.Code, e.Message)
}
// Unwrap 实现 errors.Unwrap 支持错误链解包
func (e *FileError) Unwrap() error {
return e.Err
}
// 预定义错误(使用 sentinel error 规范)
var ErrFileNotFound = errors.New("file not found")
// ------------------------------
// 业务服务实现
// ------------------------------
// FileService 基础文件服务
func FileService(ctx context.Context, filename string) error {
// 模块名称常量
const module = "FileService"
// 1. 空文件名校验(400
if filename == "" {
return &FileError{
Code: 400,
Module: module,
Message: "filename cannot be empty",
}
}
// 2. 文件不存在模拟(404
return &FileError{
Code: 404,
Module: module,
Message: "file does not exist",
Err: ErrFileNotFound, // 包装原始哨兵错误
}
}
// AdvancedFileService 高级文件服务(包装基础服务)
func AdvancedFileService(ctx context.Context, filename string) error {
// 调用底层服务并包装错误信息
if err := FileService(ctx, filename); err != nil {
return fmt.Errorf("advanced file service failed: %s %w", filename, err)
}
return nil
}
// ------------------------------go vet
// 测试用例
// ------------------------------
func TestFileService(t *testing.T) {
ctx := context.Background()
// 子测试用例(清晰分组,便于定位失败)
t.Run("file not found returns 404 error", func(t *testing.T) {
err := FileService(ctx, "test.txt")
// 断言哨兵错误
if !errors.Is(err, ErrFileNotFound) {
t.Fatalf("期望错误: %v, 实际错误: %v", ErrFileNotFound, err)
}
// 断言自定义错误类型与字段
var fErr *FileError
if !errors.As(err, &fErr) {
t.Fatal("期望返回 *FileError 类型")
}
if fErr.Code != 404 {
t.Errorf("错误码期望 404, 实际 %d", fErr.Code)
}
if fErr.Module != "FileService" {
t.Errorf("模块期望 FileService, 实际 %s", fErr.Module)
}
})
t.Run("empty filename returns 400 error", func(t *testing.T) {
err := FileService(ctx, "")
if err == nil {
t.Fatal("空文件名必须返回错误")
}
var fErr *FileError
if !errors.As(err, &fErr) {
t.Fatal("期望返回 *FileError 类型")
}
if fErr.Code != 400 {
t.Errorf("错误码期望 400, 实际 %d", fErr.Code)
}
})
}
func TestErrorWrapping(t *testing.T) {
original := errors.New("original error")
wrapped := fmt.Errorf("wrapped: %w", original)
// 测试 errors.Is 错误链匹配
if !errors.Is(wrapped, original) {
t.Error("errors.Is 应当匹配包装后的错误")
}
// 测试 errors.Unwrap 解包
unwrapped := errors.Unwrap(wrapped)
if unwrapped != original {
t.Errorf("解包后应当返回原始错误, 实际: %v", unwrapped)
}
}
func TestAdvancedFileService(t *testing.T) {
ctx := context.Background()
err := AdvancedFileService(ctx, "nonexistent.txt")
if err == nil {
t.Fatal("必须返回错误")
}
// 错误信息包含文件名
if !strings.Contains(err.Error(), "nonexistent.txt") {
t.Errorf("错误信息应当包含文件名, 实际: %v", err)
}
// 错误链中能找到自定义 FileError
var fErr *FileError
if !errors.As(err, &fErr) {
t.Error("错误链中应当包含 *FileError")
}
}