Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
時の止め方を考える
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
tenntenn - Takuya Ueda
PRO
October 23, 2024
Technology
130
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
時の止め方を考える
Go Connect#3で発表した資料です。
https://gotalk.connpass.com/event/331992/
tenntenn - Takuya Ueda
PRO
October 23, 2024
More Decks by tenntenn - Takuya Ueda
See All by tenntenn - Takuya Ueda
(続)生成AIによる 静的解析ツールの自動生成
tenntenn
PRO
0
82
まずはイテレータ(range over func)の仕様を学ぼう
tenntenn
PRO
0
480
モチベーションの維持と世代交代から考える持続可能なコミュニティ運営
tenntenn
PRO
0
180
fmfm🤔 fmtパッケージ - tenntenn.go#4
tenntenn
PRO
0
240
あなたの知らない go listコマンド - tenntenn.go#4
tenntenn
PRO
1
1.5k
ライブペアプログラミング その2 - tenntenn.go#3
tenntenn
PRO
0
99
sync.Onceを完全に理解する - tenntenn.go#3
tenntenn
PRO
0
590
Go 1.18で追加されるstrings/bytes.Cutと(*sync.Mutex).TryLockについて - Go1.18リリースパーティ
tenntenn
PRO
0
690
io/fsパッケージを読む - tenntenn.go#2
tenntenn
PRO
0
280
Other Decks in Technology
See All in Technology
10年目を迎えた「ABEMA」がどのように AI 活用を推進して、AI 駆動開発にシフトしているのか / How ABEMA, entering its 10th year, is promoting the use of AI and shifting toward AI-driven development
miyukki
0
380
AI時代の開発生産性は、個人技からチーム設計へ
moongift
PRO
4
2.6k
JAWS_ICEBERG_BASECAMP
iqbocchi
2
110
発表と総括 / Presentations and Summary
ks91
PRO
0
200
Type-safe IaC for Dart
coborinai
0
180
複数プロダクトで進めるAI機能実装 ── 実践から得たリアルな学びとロードマップ実現への挑戦 / AICon2026_yanari
rakus_dev
1
280
データエンジニアリングとドメイン駆動設計
masuda220
PRO
14
2.6k
それでも、技術なブログを書く理由 #kichijojipm / Why I Still Write Tech Blogs Even Now
shinkufencer
0
720
キャリアLT会#3
beli68
2
260
VPCセキュリティ対応の最新事情
nagisa53
1
320
Webの技術とガジェットで子どもも大人も楽しめるワクワク体験を提供する / Qiita Tech Festa Day 2026
you
PRO
1
270
論語・武士道・産業革命から見る かわるもの、かわらないもの
ichimichi
5
610
Featured
See All Featured
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
220
The #1 spot is gone: here's how to win anyway
tamaranovitovic
3
1.1k
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
67
56k
How People are Using Generative and Agentic AI to Supercharge Their Products, Projects, Services and Value Streams Today
helenjbeal
1
240
Bootstrapping a Software Product
garrettdimon
PRO
307
120k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.9k
Balancing Empowerment & Direction
lara
6
1.2k
Facilitating Awesome Meetings
lara
57
7k
WCS-LA-2024
lcolladotor
0
750
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
2.9k
Designing for Timeless Needs
cassininazir
1
400
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
380
Transcript
The Go gopher was designed by Renée French. The gopher
stickers was made by Takuya Ueda. Licensed under the Creative Commons 3.0 Attributions license. 時の止め方を考える 2024/10/22 Go Connect #3 https://tenn.in/ctxtime
UEDA Takuya / tenntenn 上田拓 也 newmo株式会社 / ソフトウェアエンジニア 一般社団法人
Gophers Japan 代表理事 Google Developers Expert (GDE) / Go Category tenntenn Conference 主催・登壇者 2013年よりGo Conferenceの運営を行う。 2016年、メルカリグループに入社、Goコミュニティへの貢献や採用・社内教育などに従事。 2021年、一般社団法人Gophers Japan設立、代表理事に就任。Google Developers Expert (GDE)に選出。 2022年、株式会社ナレッジワーク入社。ソフトウェアエンジニアおよびGoエンジニアのイネーブルメントに従事。 2024年、newmo株式会社入社。ソフトウェアエンジニアとしてプロダクト開発に従事。 略歴
func Now(context.Context) time.Time { return time.Now() } compilable compile error
1 2 Q
時を止めるとは? ▪ 時刻を使ったコードのテストがしたい • 特定の時刻で固定する package greet func Greet() string
{ now := time.Now() switch h := now.Hour() { case h >= 4 && h <= 9: return "おはよう" case h >= 10 && h <= 16: return "こんにちは" default: return "こんばんは" } }
よくある時の止め方 ▪ 引数に現在時刻を渡す ▪ 関数やインタフェースで差し替える • パッケージ変数やフィールドで持つ ▪ コンテキストに現在時刻を設定する •
context.WithValue関数を使う
やんちゃな時の止め方 - testtime func Now() $GOROOT/src/time/time.go testtime 静的解析 func Now()
$GOPATH/testtime/xxxx/time.go 書き換え go test -overlay 利用 参考:https://tenntenn.dev/ja/posts/2021-07-06-testtime/
静的解析による time.Now関数さがし ▪ GOROOTを取得する • go env GOROOTコマンドを実行 • runtime.GOROOT関数ではコンパイル時のGOROOTになりダメ
▪ パッケージの情報を取得 • (*build.Context).Importメソッドで取得できる • Context.GOROOTフィールドを変更しておく必要がある • パッケージがあるディレクトリのパスを取得 ▪ timeパッケージを構文解析する • parser.ParseDir関数でパースしてAST(抽象構文木を取得) ▪ ソースコードを変更する • ASTを変更する • x/tools/go/astutil.AddImport関数をimport宣言を追加する • go/format.Node関数で出力する
-overlayオプション ▪ ビルド時にファイルを変更する • Overlay JSONのパスを指定する • パスを読み替えることができる ▪ testtimeの実行結果を指定する
• 作成したOverlay JSONのパスをstdoutに表示する • go test -overlay $(testtime)で入れ替えることができる { "Replace": { "/usr/local/go/src/time/time.go": "/Users/tenntenn/go/pkg/testtime/time_go1.16.go" } }
//go:linkname ▪ リンカでシンボルを読み替える • 外部パッケージのシンボル(識別子)を参照できる • Go1.23からstdへのlinknameは禁止になった ◦ ハンドシェイクしてあればOK ▪
自作自演でハンドシェイク • -overlayで上書きするのでハンドシェイクを加える ▪ 共通のsync.Mapを置く • timeパッケージに宣言を追加 • testtimeパッケージがlinknameしておく
ゴルーチンの ID ▪ スタックトレースを自力でパースして取得する • いつ変更されるか分からないので真似してはダメ func goroutineID() string {
var buf [64]byte n := runtime.Stack(buf[:], false) // 10: len("goroutine ") for i := 10; i < n; i++ { if buf[i] == ' ' { return string(buf[10:i]) } } return "" }
testtimeの使い所 ▪ すでにそれなりの規模のプロジェクト • 散らばったtime.Now()を止められない ▪ やんちゃさを暖かく見守れる心 • -overlayとlinknameとゴルーチンのIDを使っている •
テストだし、まぁ最悪いいかの心 • testtime.Overlay()で-overlayしてるか取得できる ◦ 普段はt.Skipで飛ばしてほしい
まじめな時の止め方 - ctxtime ▪ コンテキストに現在時刻を設定する • テストの時だけNow関数を入れ替える ctxtime.Now internal.Now ctxtimetest
呼び出し internal.DefaultNow 呼び出し 入れ替え ctxtimetest.nowForTest 呼び出し 参考:https://tech.newmo.me/entry/2024/09/20/133402
Contextがあれば後でどうにでもなる ▪ newmo入社時に見つけたコード • よくわかってる人(yuki-ito)が書いたと思われるコード ◦ あとで困ることを経験上知っている func Now(_ context.Context)
time.Time { return time.Now().In(time.UTC) }
テストのときだけ挙動を変える ▪ internalを使った3pkg方式 • ctxtimeパッケージはtestingパッケージに依存したくない • internalパッケージに実装を置いておき変更可能にしておく ◦ パッケージ関数ではなく変数にしておく •
ctxtimetestパッケージをimportするとinit関数で入れ替える ◦ testing.Testing関数でテストか判定できる ctxtime.Now internal.Now ctxtimetest 呼び出し internal.DefaultNow 呼び出し 入れ替え ctxtimetest.nowForTest 呼び出し
testid ▪ テストに個別のIDをふる • https://github.com/newmo-oss/testid • (*testing.T).Nameメソッドでテスト名は取得できる ◦ 子テストも含めて •
テスト関数より多くIDが振りたくなることを見越して作成 ▪ コンテキストに仕込む • ミドルウェアやインタセプターで仕込む • ログやらいろんなところでテストが識別できて便利
Linterなきルールはルールではない ▪ time.Now関数を呼び出している箇所を見つける func run(pass *analysis.Pass) (any, error) { in
:= pass.ResultOf[ssainspect.Analyzer].(*ssainspect.Inspector) timenow, _ := analysisutil.ObjectOf(pass, "time", "Now").(*types.Func) if timenow == nil { return nil, nil } for in.Next() { c := in.Cursor() if analysisutil.Called(c.Instr, nil, timenow) { pass.Reportf(c.Instr.Pos(), "do not use %s, use ctxtime.Now", timenow.FullName()) } } return nil, nil }
まとめ ▪ 時を止めたい • テストで時を固定したい ▪ 最初からちゃんと考慮する • コンテキストがあればどうにかなる!
宣伝① まだ資料つくってないので、時を止めたいッッ
宣伝② @newmotech
宣伝③ tenn.in/cfc25