Skip to content

テスト補助

DD はテストシナリオ用に LoggerRecorder を提供し、ログエントリをキャプチャしてアサーションに使用できます。

LoggerRecorder

スレッドセーフなログレコーダー。テストでログ出力をキャプチャして検査します。

テキストフォーマット解析の制限

テキストモードのパーサーはデフォルトの時間フォーマット(ISO 8601)とデフォルトレベル文字列(DEBUG/INFO/WARN/ERROR/FATAL)を前提としています。TimeFormat をカスタマイズした場合、テキストモードでレベルとタイムスタンプが正しく抽出されない場合があります。カスタムフォーマットの場合は JSON フォーマット(FormatJSON)の使用を推奨します。SetFormat で設定可能です。

作成

go
recorder := dd.NewLoggerRecorder()

コアメソッド

メソッドシグネチャ説明
Writer() io.Writerio.Writer を取得
SetFormat(format LogFormat)ログフォーマットを設定(解析用)
NewLogger(cfg ...Config) (*Logger, error)このレコーダーに書き込む Logger を作成
Entries() []LogEntry全ログエントリを取得
Count() intエントリ数
Clear()全エントリをクリア
HasEntries() boolエントリが存在するか
LastEntry() *LogEntry最新のエントリ(nil セーフ)

アサーションメソッド

メソッドシグネチャ説明
EntriesAtLevel(level LogLevel) []LogEntryレベルでエントリをフィルタリング
ContainsMessage(msg string) bool指定メッセージを含むか(完全一致または部分一致)
ContainsField(key string) bool指定フィールドを含むか
GetFieldValue(key string) any最初に一致したフィールドの値を取得

使用例

基本的なテスト

go
func TestLogger(t *testing.T) {
    rec := dd.NewLoggerRecorder()
    logger, _ := rec.NewLogger()

    logger.Info("hello")
    logger.Warn("warning")

    if rec.Count() != 2 {
        t.Errorf("expected 2 entries, got %d", rec.Count())
    }

    if !rec.ContainsMessage("hello") {
        t.Error("should contain 'hello'")
    }
}

レベルアサーション

go
func TestLogLevel(t *testing.T) {
    rec := dd.NewLoggerRecorder()
    // 注意:Recorder は ISO 8601 タイムスタンプでレベルを解析するが、DevelopmentConfig の
    // 時間フォーマット("15:04:05.000")は非互換のため、DefaultConfig で手動 DEBUG 設定。
    cfg := dd.DefaultConfig()
    cfg.Level = dd.LevelDebug
    logger, _ := rec.NewLogger(cfg)

    logger.Debug("debug")
    logger.Info("info")
    logger.Error("error")

    errors := rec.EntriesAtLevel(dd.LevelError)
    if len(errors) != 1 {
        t.Errorf("expected 1 error, got %d", len(errors))
    }

    debugs := rec.EntriesAtLevel(dd.LevelDebug)
    if len(debugs) != 1 {
        t.Errorf("expected 1 debug, got %d", len(debugs))
    }
}

構造化フィールドアサーション

go
func TestStructuredLog(t *testing.T) {
    rec := dd.NewLoggerRecorder()
    logger, _ := rec.NewLogger()

    logger.InfoWith("user login",
        dd.String("user", "admin"),
        dd.String("ip", "192.168.1.1"),
    )

    if !rec.ContainsField("user") {
        t.Error("should contain 'user' field")
    }

    user := rec.GetFieldValue("user")
    if user != "admin" {
        t.Errorf("expected user=admin, got %v", user)
    }
}

最後のログエントリ

go
func TestLastEntry(t *testing.T) {
    rec := dd.NewLoggerRecorder()
    logger, _ := rec.NewLogger()

    logger.Info("first")
    logger.Error("second")

    last := rec.LastEntry()
    if last.Level != dd.LevelError {
        t.Errorf("expected Error level, got %v", last.Level)
    }
    if last.Message != "second" {
        t.Errorf("expected 'second', got %s", last.Message)
    }
}

LogEntry

キャプチャされたログエントリ構造。

go
type LogEntry struct {
    Level     LogLevel
    Message   string
    Fields    []Field
    Timestamp time.Time
    Format    LogFormat
    RawOutput string
}

次のステップ