golang实现简单模拟时钟功能的测试工具插件库clockwork的使用
Golang实现简单模拟时钟功能的测试工具插件库clockwork的使用
clockwork是一个简单的Go语言假时钟库,用于测试时间相关的功能。
使用方法
用clockwork.Clock
接口替换time
包的使用。
例如,不使用直接的time.Sleep
:
func myFunc() {
time.Sleep(3 * time.Second)
doSomething()
}
而是注入一个clock并使用它的Sleep方法:
func myFunc(clock clockwork.Clock) {
clock.Sleep(3 * time.Second)
doSomething()
}
测试示例
你可以很容易地用FakeClock
测试myFunc
:
func TestMyFunc(t *testing.T) {
ctx := context.Background()
c := clockwork.NewFakeClock()
// 启动我们的睡眠函数
var wg sync.WaitGroup
wg.Add(1)
go func() {
myFunc(c)
wg.Done()
}()
// 确保我们等待直到myFunc在等待时钟
// 使用context避免永远阻塞
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
c.BlockUntilContext(ctx, 1)
assertState()
// 将FakeClock向前推进时间
c.Advance(3 * time.Second)
// 等待函数完成
wg.Wait()
assertState()
}
在生产环境中,只需注入真实时钟:
myFunc(clockwork.NewRealClock())
完整示例
下面是一个完整的示例demo:
package main
import (
"context"
"fmt"
"sync"
"testing"
"time"
"github.com/jonboulle/clockwork"
)
// 模拟一个需要等待的函数
func delayedFunction(clock clockwork.Clock, duration time.Duration) {
fmt.Println("开始等待...")
clock.Sleep(duration)
fmt.Println("等待结束!")
}
func TestDelayedFunction(t *testing.T) {
// 创建假时钟
fakeClock := clockwork.NewFakeClock()
// 使用WaitGroup等待goroutine完成
var wg sync.WaitGroup
wg.Add(1)
// 启动测试函数
go func() {
delayedFunction(fakeClock, 5*time.Second)
wg.Done()
}()
// 确保函数已经开始执行并进入Sleep状态
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
fakeClock.BlockUntilContext(ctx, 1)
// 快进时间
fmt.Println("快进5秒...")
fakeClock.Advance(5 * time.Second)
// 等待函数完成
wg.Wait()
fmt.Println("测试完成")
}
func main() {
// 生产环境使用真实时钟
realClock := clockwork.NewRealClock()
fmt.Println("使用真实时钟:")
delayedFunction(realClock, 2*time.Second)
// 运行测试
fmt.Println("\n运行测试:")
TestDelayedFunction(&testing.T{})
}
特性说明
NewFakeClock()
- 创建一个新的假时钟实例Advance(duration)
- 将假时钟向前推进指定时间BlockUntilContext(ctx, n)
- 阻塞直到有n个goroutine在等待时钟NewRealClock()
- 创建一个真实时钟实例(包装标准time包)
这个库特别适合测试那些依赖于时间或定时器的代码,可以避免在测试中实际等待很长时间。
更多关于golang实现简单模拟时钟功能的测试工具插件库clockwork的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang实现简单模拟时钟功能的测试工具插件库clockwork的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 模拟时钟库 Clockwork 使用指南
Clockwork 是一个用于 Go 语言的测试工具库,它允许你在测试中模拟时间,非常适合需要控制时间因素的单元测试场景。
安装 Clockwork
go get github.com/jonboulle/clockwork
基本使用
真实时钟
package main
import (
"fmt"
"time"
"github.com/jonboulle/clockwork"
)
func main() {
// 使用真实时钟
realClock := clockwork.NewRealClock()
fmt.Println("当前时间:", realClock.Now())
// 真实时钟的 Sleep 会实际休眠
realClock.Sleep(time.Second * 2)
fmt.Println("2秒后:", realClock.Now())
}
模拟时钟
package main
import (
"fmt"
"testing"
"time"
"github.com/jonboulle/clockwork"
)
func TestWithFakeClock(t *testing.T) {
// 创建模拟时钟
fakeClock := clockwork.NewFakeClock()
startTime := fakeClock.Now()
fmt.Println("初始时间:", startTime)
// 向前推进时间
fakeClock.Advance(time.Hour * 2)
fmt.Println("2小时后:", fakeClock.Now())
// 设置特定时间
fakeClock.Set(startTime.Add(time.Hour * 24))
fmt.Println("设置为24小时后:", fakeClock.Now())
}
高级用法
定时器模拟
func TestTimer(t *testing.T) {
fakeClock := clockwork.NewFakeClock()
// 创建一个定时器,1小时后触发
timer := fakeClock.NewTimer(time.Hour)
// 立即检查定时器是否触发
select {
case <-timer.Chan():
t.Error("定时器不应该立即触发")
default:
// 预期行为
}
// 推进时间1小时
fakeClock.Advance(time.Hour)
// 再次检查定时器
select {
case <-timer.Chan():
// 预期行为
default:
t.Error("定时器应该已经触发")
}
}
Ticker 模拟
func TestTicker(t *testing.T) {
fakeClock := clockwork.NewFakeClock()
ticker := fakeClock.NewTicker(time.Minute)
// 推进时间并检查ticker
fakeClock.Advance(time.Minute * 3)
// 应该收到3次tick
for i := 0; i < 3; i++ {
select {
case <-ticker.Chan():
// 预期行为
default:
t.Errorf("应该收到第%d个tick", i+1)
}
}
}
在测试中使用
func TestExpiredCache(t *testing.T) {
fakeClock := clockwork.NewFakeClock()
cache := NewCache(fakeClock)
// 添加一个1小时后过期的条目
cache.Set("key", "value", time.Hour)
// 立即检查 - 应该存在
if !cache.Has("key") {
t.Error("条目应该存在")
}
// 推进时间1小时1分钟
fakeClock.Advance(time.Hour + time.Minute)
// 现在条目应该过期
if cache.Has("key") {
t.Error("条目应该已过期")
}
}
实际应用示例
type RateLimiter struct {
clock clockwork.Clock
interval time.Duration
lastCall time.Time
}
func NewRateLimiter(interval time.Duration, clock clockwork.Clock) *RateLimiter {
return &RateLimiter{
clock: clock,
interval: interval,
}
}
func (r *RateLimiter) Allow() bool {
now := r.clock.Now()
if now.Sub(r.lastCall) >= r.interval {
r.lastCall = now
return true
}
return false
}
func TestRateLimiter(t *testing.T) {
fakeClock := clockwork.NewFakeClock()
limiter := NewRateLimiter(time.Second, fakeClock)
// 第一次应该允许
if !limiter.Allow() {
t.Error("第一次调用应该允许")
}
// 立即再次调用应该拒绝
if limiter.Allow() {
t.Error("立即再次调用应该拒绝")
}
// 推进时间1秒后应该允许
fakeClock.Advance(time.Second)
if !limiter.Allow() {
t.Error("1秒后应该允许")
}
}
总结
Clockwork 提供了以下主要功能:
- 模拟时间推进
- 模拟定时器和ticker
- 设置特定时间点
- 完全替代标准库的time包功能
使用 Clockwork 可以让你在测试中精确控制时间因素,避免测试依赖于实际时间流逝,使测试更加可靠和快速。