testing

package
v0.0.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 2, 2026 License: MIT Imports: 9 Imported by: 0

README

testing 包 — 测试框架

所属层级: Infrastructure Layer
设计理念: 集成测试支持,Mock 对象
设计灵感: Spring Test + Mockito

概述

testing 包提供完整的测试支持,合并了原 testtestcontextmock 包的功能。

核心功能
功能 说明
TestRunner 集成测试运行器,支持自动启动应用上下文和依赖注入
TestContext 测试上下文,集成 IoC 容器和测试配置
Mock Mock 对象支持,用于测试中的依赖隔离
断言工具 丰富的断言函数简化测试验证
Web 测试客户端 简化的 HTTP 请求测试
泛型辅助 类型安全的 Bean 获取和验证

核心接口

TestRunner 测试运行器
type TestRunner struct {
    // ...
}
创建
runner := testing.NewTestRunner(t,
    testing.WithTestAppName("my-test-app"),
    testing.WithProperty("db.url", "localhost:5432"),
    testing.WithMockBean("userService", mockUserService),
)
配置选项
选项 说明
WithProperty(key, value) 设置测试属性
WithMockBean(name, bean) 添加 Mock Bean
WithoutAutoConfig() 禁用自动配置
WithTestAppName(name) 设置应用名称
运行测试
runner.Run(func(ctx *testing.TestContext) {
    service := ctx.Get("myService").(*MyService)
    result := service.DoSomething()
    testing.AssertEqual(t, "expected", result)
})
TestContext 测试上下文
type TestContext struct {
    // ...
}
创建
ctx := testing.NewTestContext(t)
ctx.Register(reflect.TypeOf(&MyService{}), func(c core.Container) (any, error) {
    return &MyService{}, nil
})

service := ctx.Get("myService").(*MyService)
便捷函数
函数 说明
Test(t, fn) 直接运行测试
TestWithContainer(t, container, fn) 使用指定容器运行测试
SetupTest(t, fn) 设置和清理测试
RunSubtest(t, name, fn) 运行子测试
Parallel(t, tests) 并行运行测试
// 直接运行测试
testing.Test(t, func(ctx *testing.TestContext) {
    // 测试逻辑
})

// 使用指定容器
testing.TestWithContainer(t, container, func(ctx *testing.TestContext) {
    // 测试逻辑
})

// 设置和清理
ctx := testing.SetupTest(t, func(ctx *testing.TestContext) {
    // 初始化
})

// 子测试
testing.RunSubtest(t, "subtest-name", func(ctx *testing.TestContext) {
    // 子测试逻辑
})

// 并行测试
testing.Parallel(t, map[string]func(ctx *testing.TestContext){
    "test1": func(ctx *testing.TestContext) { /* ... */ },
    "test2": func(ctx *testing.TestContext) { /* ... */ },
})
泛型辅助
// 必须获取 Bean
service := testing.MustGet[MyService](ctx, "myService")

// 安全获取 Bean
service, err := testing.GetBean[MyService](ctx, "myService")
Mock 对象
type Mock struct {
    // ...
}
创建和使用
m := testing.NewMock()
m.Expect("GetUser", []any{1}, &User{Name: "Alice"}, nil)

result, err := m.Call("GetUser", 1)
testing.AssertNoError(t, err)

// 验证期望
testing.AssertExpectations(t, m)
指定调用次数
m.ExpectTimes("Log", []any{"message"}, nil, nil, 3)
链式调用
recorder := testing.NewMockRecorder(m)
recorder.Return(&User{}, nil).Times(2)
使用 WithMock
testing.WithMock(t, func(ctx *testing.TestContext, mock *testing.MockRecorder) {
    // 测试逻辑
})
断言工具
函数 说明
Assert(t, condition, msg) 基础断言
AssertEqual(t, expected, actual) 相等断言
AssertNoError(t, err) 无错误断言
AssertError(t, err) 有错误断言
AssertNil(t, value) 空值断言
AssertNotNil(t, value) 非空值断言
AssertTrue(t, condition) 真值断言
AssertFalse(t, condition) 假值断言
testing.Assert(t, condition, "message")
testing.AssertEqual(t, expected, actual)
testing.AssertEqual(t, expected, actual, "custom message")
testing.AssertNoError(t, err)
testing.AssertError(t, err)
testing.AssertNil(t, value)
testing.AssertNotNil(t, value)
testing.AssertTrue(t, condition)
testing.AssertFalse(t, condition)
跳过测试
testing.SkipIf(t, runtime.GOOS == "windows", "not supported on Windows")
TestWebClient Web 测试客户端
type TestWebClient struct {
    // ...
}
创建和使用
client := testing.NewTestWebClient(t, "http://localhost:8080")

resp := client.Get("/api/users")
resp.AssertStatus(t, 200)
resp.AssertBody(t, `{"status":"ok"}`)

快速开始

基本单元测试
package main

import (
    "testing"
    "github.com/xudefa/enhance/testing"
)

func TestMyService(t *testing.T) {
    testing.Test(t, func(ctx *testing.TestContext) {
        service := &MyService{}
        result := service.DoSomething()
        testing.AssertEqual(t, "expected", result)
    })
}

API 参考

完整集成测试
func TestUserService_Integration(t *testing.T) {
    runner := testing.NewTestRunner(t,
        testing.WithTestAppName("user-service-test"),
        testing.WithProperty("db.url", "localhost:5432/test"),
    )

    runner.Run(func(ctx *testing.TestContext) {
        // 获取 Bean
        service := testing.MustGet[UserService](ctx, "userService")

        // 执行测试
        user, err := service.GetUser(1)
        testing.AssertNoError(t, err)
        testing.AssertNotNil(t, user)
        testing.AssertEqual(t, "Alice", user.Name)
    })
}
单元测试 + Mock
func TestOrderService_WithMock(t *testing.T) {
    // 创建 Mock
    paymentMock := testing.NewMock()
    paymentMock.Expect("ProcessPayment", []any{100.0}, true, nil)

    // 运行测试
    testing.Test(t, func(ctx *testing.TestContext) {
        service := NewOrderService(paymentMock)
        
        err := service.PlaceOrder(100.0)
        testing.AssertNoError(t, err)

        // 验证 Mock 调用
        testing.AssertExpectations(t, paymentMock)
    })
}
并行测试
func TestParallel(t *testing.T) {
    testing.Parallel(t, map[string]func(ctx *testing.TestContext){
        "test_create": func(ctx *testing.TestContext) {
            // 创建测试
        },
        "test_update": func(ctx *testing.TestContext) {
            // 更新测试
        },
        "test_delete": func(ctx *testing.TestContext) {
            // 删除测试
        },
    })
}

使用示例

Web API 测试
func TestUserAPI(t *testing.T) {
    // 启动测试服务器
    runner := testing.NewTestRunner(t,
        testing.WithTestAppName("api-test"),
        testing.WithProperty("server.port", "0"), // 随机端口
    )

    runner.Run(func(ctx *testing.TestContext) {
        client := testing.NewTestWebClient(t, ctx.GetServerURL())

        // 测试 GET 请求
        resp := client.Get("/api/users/1")
        resp.AssertStatus(t, 200)
        resp.AssertJSONPath(t, "$.name", "Alice")

        // 测试 POST 请求
        resp = client.Post("/api/users", `{"name": "Bob"}`)
        resp.AssertStatus(t, 201)
    })
}
子测试
func TestUserService(t *testing.T) {
    testing.RunSubtest(t, "GetUser", func(ctx *testing.TestContext) {
        service := testing.MustGet[UserService](ctx, "userService")
        user, err := service.GetUser(1)
        testing.AssertNoError(t, err)
        testing.AssertNotNil(t, user)
    })

    testing.RunSubtest(t, "CreateUser", func(ctx *testing.TestContext) {
        service := testing.MustGet[UserService](ctx, "userService")
        user, err := service.CreateUser("Bob")
        testing.AssertNoError(t, err)
        testing.AssertEqual(t, "Bob", user.Name)
    })
}

最佳实践

1. 使用 TestRunner 管理测试生命周期
// ✅ 推荐:使用 TestRunner 自动管理生命周期
runner := testing.NewTestRunner(t,
    testing.WithTestAppName("my-test"),
)
runner.Run(func(ctx *testing.TestContext) {
    // 测试逻辑
})

// ⚠️ 不推荐:手动管理容器
container := core.NewContainer()
// 手动注册和清理
2. 使用 Mock 隔离外部依赖
// ✅ 推荐:使用 Mock 隔离外部服务
paymentMock := testing.NewMock()
paymentMock.Expect("ProcessPayment", []any{100.0}, true, nil)

service := NewOrderService(paymentMock)
err := service.PlaceOrder(100.0)
testing.AssertNoError(t, err)
testing.AssertExpectations(t, paymentMock)

// ⚠️ 不推荐:依赖真实外部服务
service := NewOrderService(realPaymentGateway)
3. 使用泛型获取类型安全的 Bean
// ✅ 推荐:使用泛型获取 Bean
service := testing.MustGet[UserService](ctx, "userService")

// ⚠️ 不推荐:使用类型断言
service := ctx.Get("userService").(*UserService)
4. 使用并行测试提升性能
// ✅ 推荐:独立测试并行执行
testing.Parallel(t, map[string]func(ctx *testing.TestContext){
    "test_create": func(ctx *testing.TestContext) { /* ... */ },
    "test_update": func(ctx *testing.TestContext) { /* ... */ },
})

// ⚠️ 不推荐:所有测试串行执行
func TestAll(t *testing.T) {
    testCreate(t)
    testUpdate(t)
    testDelete(t)
}
5. 使用断言工具简化验证
// ✅ 推荐:使用断言工具
testing.AssertEqual(t, "expected", actual)
testing.AssertNoError(t, err)
testing.AssertNotNil(t, user)

// ⚠️ 不推荐:手动编写断言逻辑
if actual != "expected" {
    t.Errorf("expected %s, got %s", "expected", actual)
}
6. 设计要点
  • TestRunner 自动管理应用生命周期
  • Mock 对象线程安全,支持并发测试
  • 断言函数自动标记为 Helper,显示正确的行号
  • 清理函数按注册逆序执行
  • 零外部依赖(除框架核心包外)

Documentation

Overview

Package testing 提供测试工具支持,用于 enhance 框架。

该模块提供测试辅助函数、模拟对象创建、测试断言等测试相关功能,简化单元测试和集成测试的编写。

架构设计

  • TestingT: 测试接口,兼容 testing.T
  • TestRunner: 测试运行器接口,用于运行测试
  • TestContext: 测试上下文接口,提供测试环境
  • Mock: Mock 对象接口,用于设置期望和验证调用

核心功能

  • 断言函数: 提供 Equal、NotNil、True、False 等常用断言
  • 模拟对象: 支持创建模拟依赖对象
  • 测试辅助: 提供临时文件创建、随机数据生成等辅助函数
  • 测试超时: 支持设置测试超时时间

使用方式

使用断言:

func TestUserService(t *testing.T) {
    user := service.GetUser(1)
    testing.AssertNotNil(t, user, "user should not be nil")
    testing.AssertEqual(t, "John", user.Name, "name should match")
}

使用测试辅助:

func TestFileProcessing(t *testing.T) {
    tmpFile := testing.CreateTempFile(t, "test content")
    defer tmpFile.Close()
    // 测试文件处理逻辑
}

断言函数

  • AssertEqual: 断言两个值相等
  • AssertNotEqual: 断言两个值不相等
  • AssertNil: 断言值为 nil
  • AssertNotNil: 断言值不为 nil
  • AssertTrue: 断言条件为 true
  • AssertFalse: 断言条件为 false
  • AssertContains: 断言字符串包含子串
  • AssertPanics: 断言函数会 panic

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Assert

func Assert(t *testing.T, condition bool, msg string)

Assert 断言条件为真。

func AssertEqual

func AssertEqual(t *testing.T, expected, actual any, msg ...string)

AssertEqual 断言两个值相等。

func AssertError

func AssertError(t *testing.T, err error, msg ...string)

AssertError 断言有错误。

func AssertExpectations

func AssertExpectations(t TestingT, mock Mock) bool

AssertExpectations 断言 Mock 期望是否满足。

func AssertFalse

func AssertFalse(t *testing.T, condition bool, msg ...string)

AssertFalse 断言条件为假。

func AssertNil

func AssertNil(t *testing.T, value any, msg ...string)

AssertNil 断言值为 nil。

func AssertNoError

func AssertNoError(t *testing.T, err error, msg ...string)

AssertNoError 断言无错误。

func AssertNotNil

func AssertNotNil(t *testing.T, value any, msg ...string)

AssertNotNil 断言值不为 nil。

func AssertTrue

func AssertTrue(t *testing.T, condition bool, msg ...string)

AssertTrue 断言条件为真。

func GetByType

func GetByType[T any](ctx TestContext) (T, error)

GetByType 获取指定类型的 Bean。

func MustGetByType

func MustGetByType[T any](ctx TestContext) T

MustGetByType 必须获取指定类型的 Bean,否则测试失败。

func Parallel

func Parallel(t *testing.T, tests map[string]func(ctx TestContext))

Parallel 并行运行多个测试。

func RunSubtest

func RunSubtest(t *testing.T, name string, fn func(ctx TestContext))

RunSubtest 运行子测试。

func SkipIf

func SkipIf(t *testing.T, condition bool, reason string)

SkipIf 满足条件时跳过测试。

func TeardownTest

func TeardownTest(ctx TestContext, teardown func(ctx TestContext))

TeardownTest 清理测试环境。

func Test

func Test(t *testing.T, fn func(ctx TestContext))

Test 运行测试函数。

func TestWithContainer

func TestWithContainer(t *testing.T, container core.Container, fn func(ctx TestContext))

TestWithContainer 使用指定容器运行测试。

func WithMock

func WithMock(t *testing.T, fn func(ctx TestContext, mock *MockRecorder))

WithMock 使用 Mock 运行测试。

Types

type Expectation

type Expectation struct {
	Method    string
	Args      []any
	Result    any
	Error     error
	Times     int
	CallCount int
}

Expectation 表示一个方法调用期望。

type Mock

type Mock interface {
	// Expect 设置方法调用期望,默认期望调用 1 次。
	Expect(method string, args []any, result any, err error) Mock

	// ExpectTimes 设置方法调用期望,指定期望调用次数。
	ExpectTimes(method string, args []any, result any, err error, times int) Mock

	// Call 模拟方法调用,返回匹配的期望结果。
	Call(method string, args ...any) (any, error)

	// Verify 验证所有期望是否满足。
	Verify() error

	// Reset 重置 Mock 对象的所有状态。
	Reset()
}

Mock 模拟对象接口。

用于设置方法调用期望和验证调用是否满足。

func NewMock

func NewMock() Mock

NewMock 创建一个新的 Mock 对象。

type MockRecorder

type MockRecorder struct {
	// contains filtered or unexported fields
}

MockRecorder Mock 记录器,用于链式设置期望。

func NewMockRecorder

func NewMockRecorder(mock Mock) *MockRecorder

NewMockRecorder 创建 Mock 记录器。

func (*MockRecorder) Return

func (r *MockRecorder) Return(result any, err error) Mock

Return 设置返回值(链式调用)。

func (*MockRecorder) Times

func (r *MockRecorder) Times(n int) Mock

Times 设置调用次数(链式调用)。

type TestConfig

type TestConfig struct {
	Properties map[string]any
	MockBeans  map[string]any
	AutoConfig bool
	AppName    string
}

TestConfig 测试配置

type TestContext

type TestContext interface {
	// T 获取底层测试对象。
	T() TestingT

	// GetByType 从容器按类型获取 Bean,如果获取失败则测试失败。
	GetByType(t reflect.Type) any

	// Register 向容器注册 Bean。
	Register(name string, bean any)

	// SetProperty 设置测试属性。
	SetProperty(key string, value any)

	// GetProperty 获取测试属性。
	GetProperty(key string) any

	// AddCleanup 添加测试清理函数。
	AddCleanup(fn func())

	// Cleanup 执行所有清理函数。
	Cleanup()

	// Close 关闭测试上下文。
	Close()

	// Container 获取 IoC 容器。
	Container() any
}

TestContext 测试上下文接口。

提供测试环境,包括 IoC 容器、测试配置、清理函数等。

func NewTestContext

func NewTestContext(t *testing.T) TestContext

NewTestContext 创建新的测试上下文。

func SetupTest

func SetupTest(t *testing.T, setup func(ctx TestContext)) TestContext

SetupTest 设置测试环境。

type TestOption

type TestOption func(*TestConfig)

TestOption 测试选项

func WithMockBean

func WithMockBean(name string, bean any) TestOption

WithMockBean 添加 Mock Bean

func WithProperty

func WithProperty(key string, value any) TestOption

WithProperty 设置测试属性

func WithTestAppName

func WithTestAppName(name string) TestOption

WithTestAppName 设置应用名称

func WithoutAutoConfig

func WithoutAutoConfig() TestOption

WithoutAutoConfig 禁用自动配置

type TestResponse

type TestResponse struct {
	// contains filtered or unexported fields
}

TestResponse 测试响应

func (*TestResponse) AssertBody

func (r *TestResponse) AssertBody(t *testing.T, expected string)

AssertBody 断言响应体

func (*TestResponse) AssertStatus

func (r *TestResponse) AssertStatus(t *testing.T, expected int)

AssertStatus 断言状态码

func (*TestResponse) Body

func (r *TestResponse) Body() []byte

Body 获取响应体

func (*TestResponse) Header

func (r *TestResponse) Header(name string) string

Header 获取响应头

func (*TestResponse) StatusCode

func (r *TestResponse) StatusCode() int

StatusCode 获取状态码

type TestRunner

type TestRunner struct {
	// contains filtered or unexported fields
}

TestRunner 测试运行器

func NewTestRunner

func NewTestRunner(t *testing.T, opts ...TestOption) *TestRunner

NewTestRunner 创建测试运行器

func (*TestRunner) GetContext

func (r *TestRunner) GetContext() TestContext

GetContext 获取测试上下文

func (*TestRunner) Run

func (r *TestRunner) Run(fn func(TestContext))

Run 运行测试

type TestWebClient

type TestWebClient struct {
	// contains filtered or unexported fields
}

TestWebClient Web 测试客户端

func NewTestWebClient

func NewTestWebClient(t *testing.T, baseURL string) *TestWebClient

NewTestWebClient 创建 Web 测试客户端

func (*TestWebClient) Get

func (c *TestWebClient) Get(path string) *TestResponse

Get 发送 GET 请求

func (*TestWebClient) Post

func (c *TestWebClient) Post(path string, body any) *TestResponse

Post 发送 POST 请求

type TestingT

type TestingT interface {
	Errorf(format string, args ...any)
	Fatalf(format string, args ...any)
	Helper()
}

TestingT 测试接口,兼容 testing.T。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL