Goã§åä½ãã¹ããå®è£ ããå ´åãåçãªè¨èªã®ããã«ããã¹ãå®è¡ä¸ã«å¤é¨ã¸ã®ä¾åãç½®ãæãããã¨ãã£ããã¨ã¯ã§ãã¾ããã代ããã«ã
- å¤é¨ã¸ã®ä¾åãå¼æ°ã§æ¸¡ã
- å¤é¨ã¸ã®ä¾åãã¤ã³ã¿ã¼ãã§ã¤ã¹ã§æ¸¡ã
ã®ããã«ããã¹ã対象ããã¹ãå¯è½ãªå®è£ ã«å¤æ´ãã¦ããããã¹ãã®æã¯å¤é¨ã¸ã®ä¾åãã¢ãã¯çã«ç½®ãæãã¦å®è¡ããå ´åãå¤ãã®ã§ã¯ãªããã¨æãã¾ãã
å人çãªä½é¨ã§ããã°ããã¹ãå¯è½ãªå®è£ ã«ç½®ãæãã¦ããéç¨ã§è¨è¨ãæ´ç·´ããã¦ãã*1ãã¨ã¯åº¦ã ããã®ã§ãé¢åãå¼·å¶ããã¦ããã¨ããããã¯è¨è¨ãæ´çããããã®éå ·ã¨ãã£ãæãæ¹ããã¦ããã®ã§ãããããã¯è¨ã£ã¦ãåçãªè¨èªã«æ¯ã¹ãã¨é¢åã ãªã¨æããã¨ãã¯å°ãªãããããã¾ããæ¢åã®å®è£ ããã¹ãå¯è½ã«ãªã£ã¦ããããå¤æ´ããã³ã¹ããé«ãå ´åã¯ç¹ã«ããã§ããã
ãããªã¨ããæ°è»½ã«ã¢ã³ãã¼ãããã§ããã¨å¬ããããããªããã¨æã£ã¦ããã¹ãã®æã ãé¢æ°ãç½®ãæãããããããªã©ã¤ãã©ãªãä½ãã¾ããã
ãã®ã©ã¤ãã©ãªã¯tenntenn/testtimeã«ã¨ã¦ãå½±é¿ãåãã¦ãã¾ãã
使ãæ¹
試ãã«æ¨æºã©ã¤ãã©ãªã® time.Now ãç½®ãæãã¾ããå ·ä½çãªã³ã¼ãã¯æ¬¡ã®ããã«ãªãã¾ãã
import ( "testing" "time" "github.lufia/plug" ) func isLeap() bool { now := time.Now() return (now.Year() % 4) == 0 // 主é¡ã§ã¯ãªãã®ã§ãããå¹´ã®å®è£ ã¯çç¥ } func TestIsLeap(t *testing.T) { scope := plug.CurrentScopeFor(t) key := plug.Func("time.Now", time.Now) plug.Set(scope, key, func() time.Time { return time.Date(2024, 5, 10, 11, 0, 0, 0, time.UTC) }) if !isLeap() { t.Errorf("2024 is a leap year") } }
plug.Func ã®ç¬¬1å¼æ°ã¯é¢æ°ã®ååãæå®ãã¾ããçç±ã¯å¾è¿°ãã¾ãããããã¯å¿ ã以ä¸ã®æ¸å¼ã§è¨è¿°ãã¦ãã ããã
- (package-path).(function-name)
- (package-path).(type-name).(method-name)
ä¾ãæãã㨠math/rand/v2.N ã net/http.Client.Do ãªã©ã§ããæ¨æºã® go doc
ãåãåãå¼æ°ã¨ä¼¼ãã¦ãã¾ãããããã±ã¼ã¸åã®çç¥ã¯ã§ãã¾ãã*2ã
ããã§ãTestIsLeap ã®ä¸ã§å®è¡ãã time.Now ã¯åºå®ã§2024å¹´5æ10æ¥ã®æå»ãè¿ãããã«ãªãã¾ããã¹ã¿ãã¯ãæããªãéãå½±é¿ã¯ç¶ãã®ã§ãisLeap é¢æ°ãå¼ã³åºã time.Time ãåºå®ã®å¤ãè¿ãã¾ãã
ãã¹ãã®å®è¡
ãã¹ããå®è¡ããã¨ãã¯ä»¥ä¸ã®ããã«å®è¡ãã¦ãã ããã-overlay
ãªãã·ã§ã³ãå¿
è¦ã§ãã
go test -overlay <(go run github.com/lufia/plug/cmd/plug@latest) # åãã¦æ¸ãã¦ããã go run github.com/lufia/plug/cmd/plug@latest >overlay.json go test -overlay overlay.json
-overlay
ãªãã·ã§ã³ã®è©³ç´°ã¯ãä¸ã§æããtenntennããã®è¨äºãèªãã§ãããã¨è¯ãã®ã§ãããããã§ã¯ä»¥ä¸ã®ãããªãã¨ãå®è¡ãã¦ãã¾ãã
- ã«ã¬ã³ããã£ã¬ã¯ããªã®ã½ã¼ã¹ã³ã¼ããã plug.Func ãæ¢ã
- plug.Func ã®ç¬¬2å¼æ°ãåçã«ç½®ãæãã§ããããã«æ¸ãæãã
- å®è¡ã¹ã¿ãã¯ã«é¢é£ã¥ããã¹ã³ã¼ããæããã¾ã§ãplug.Set ã®ç¬¬3å¼æ°ã«æ¸¡ãé¢æ°ã§ time.Now ãç½®ãæãã
- time.Now ãå¼ã³åºããã¨ããå®è¡ã¹ã¿ãã¯ãé¡ã£ã¦ç´è¿ã® time.Now ãå¼ã³åºããçµæãè¿ã
- 該å½ããé¢æ°ãå®è¡ã¹ã¿ãã¯ä¸ã§ç½®ãæãããã¦ãªããã°æ¬ç©ã®çµæãè¿ã
plug@latest ã¯ã«ã¬ã³ããã£ã¬ã¯ããªã« plug/ ã¨ãããã£ã¬ã¯ããªãä½æãã¾ãããããã¯å®è¡ãããã³ã«çæããã®ã§ãä¸è¦ã«ãªã£ããæ¶ãã¦ãåé¡ããã¾ããã
ãµããã¹ãã§é¨åçã«ç½®ãæãã
ä¸é¨ã®ãµããã¹ãå®è¡ä¸ã ããå¥ã®å¤ã«ç½®ãæãããå ´åã¯ããµããã¹ãã§åãããã«æ¸ãã¨å®ç¾ã§ãã¾ãã
func TestIsLeap(t *testing.T) { scope := plug.CurrentScopeFor(t) key := plug.Func("time.Now", time.Now) plug.Set(scope, key, func() time.Time { return time.Date(2024, 5, 10, 11, 0, 0, 0, time.UTC) }) t.Run("ãµããã¹ã", func(t *testing.T) { scope := plug.CurrentScopeFor(t) plug.Set(scope, key, func() time.Time { ... }) // ãã以éããµããã¹ãã®ä¸ã§ã¯å¥ã®å¤ãè¿ã }) // ãµããã¹ãã®å¤ã§ã¯2024å¹´5æã®æå»ãè¿ã }
ã¡ã½ãããç½®ãæãã
ã¡ã½ãããç½®ãæãã§ãã¾ãã以ä¸ã®ä¾ã§ã¯ãnet/http.Client ã® Do ã¡ã½ãããç½®ãæãã¦ããã®ã§ãhttp.Get ã«ãå½±é¿ãã¦ãã¾ãã
func TestHTTPClientGet(t *testing.T) { scope := plug.CurrentScopeFor(t) key := plug.Func("net/http.Client.Do", (*http.Client)(nil).Do) plug.Set(scope, key, func(req *http.Request) (*http.Response, error) { return &http.Response{StatusCode: 200}, nil }) resp, _ := http.Get("https://example.com") }
ã¸ã§ããªãã¯é¢æ°ãç½®ãæãã
åãã©ã¡ã¼ã¿ã®ããé¢æ°ã¯ãåãã¨ã«é¢æ°ã渡ãã¾ãã
func TestMathRand(t *testing.T) { scope := plug.CurrentScopeFor(t) key := plug.Func("math/rand/v2.N", rand.N[int]) plug.Set(scope, key, func(n int) int { return 3 }) fmt.Println(rand.N[int](10)) }
ãã®ã¨ãã rand.N[int]
㯠plug.Set ã§å·®ãæ¿ãã£ãé¢æ°ã使ããã¾ããã rand.N[int64]
ã¯ç»é²ãã¦ããªãã®ã§æ¬ç©ã®å®è£
ã使ããã¾ãã
é¢æ°ã®å¼æ°ãå¼ã³åºãåæ°ãæ¤æ»ãã
å
é¨çã«å¼ã°ããåæ°ãæã£ã¦ããã®ã§ãããã使ã£ã¦æå¾
ããéãã«å¼ã°ãã¦ããããæ¤æ»ã§ãã¾ããplug.FuncRecorder[T] ã«æ¸¡ãæ§é ä½ã®ãã£ã¼ã«ãã¯ãé¢æ°å¼æ°ã®ååã«å¯¾å¿ãããã®ã使ããã¾ãããã®ã¨ããé¢æ°å¼æ°ã®ååããã©ã³ã¯æå®å(_
)ã«ãã¦ããã¨ç¡è¦ãã¾ãã
func TestRecorder(t *testing.T) { scope := plug.CurrentScopeFor(t) key := plug.Func("os.Getenv", func(string) string { return "dummy" }) var r plug.FuncRecorder[struct { Key string `plug:"key"` }] plug.Set(scope, key, fake).SetRecorder(&r) os.Getenv("PATH") if r.Count() != 1 { t.Errorf("Count = %d; want 1", r.Count()) } if r.At(0).Key != "PATH" { t.Errorf("At(0).Key = %s; want PATH", r.At(0).Key) } }
ä»å¾ã®äºå®
å®è¡ã®ãã³ã«éç解æããã¦å¿ è¦ãªãã¡ã¤ã«ãçæãã¦ããã®ã§ãããã±ã¼ã¸ãå¤ããªã£ã¦ããã¨ææã«é ããªãã¾ããGoãã¼ã«ãã§ã¼ã³ã¨ããã±ã¼ã¸ã®ãã¼ã¸ã§ã³ãå¤ãããªããã°åºæ¬çã«ã¯çæãããã¡ã¤ã«ãåããã®ã«ãªãã®ã§ããã¾ãæé©åãã§ããã¨ããã§ããã
ä»ã«ããã¸ã§ããªãã¯åã®ã¡ã½ããã«å¯¾å¿ãããã¨ããgo build
ã§ã使ããããã«ããããªã©ãè²ã
ã¨ãããããã¨ã¯ããã¾ãã
ææ: ãªãæååã®ãã¼ãå¿ è¦ã¨ãã¦ããã
Goã§ã¯é¢æ°ãåä¸ãã©ãããæ¯è¼ãããã¨ãã§ãã¾ãããã¸ã§ããªãã¯ã§ãªãé¢æ°ã®å ´å㯠reflect.ValueOf(os.Getenv).Pointer()
ãçµç±ãããã¨ã§æ¯è¼ã§ãã¾ãããLinux/AMD64ã®å ´åã¯ã ãããæå¾
éãã«åãã¾ãã reflect.Value.Pointer ã®ããã¥ã¡ã³ãã«ã¯ä»¥ä¸ã®ããã«æ¸ããã¦ãã¾ãã
If v's Kind is Func, the returned pointer is an underlying code pointer, but not necessarily enough to identify a single function uniquely. The only guarantee is that the result is zero if and only if v is a nil func Value.
ããã«ã¸ã§ããªãã¯é¢æ°ã§ã¯ãåãã©ã¡ã¼ã¿ãã¨ã«ç°ãªãé¢æ°ãã¤ã³ã¿ãå²ãå½ã¦ãããããã§ã reflect.Value.Pointer ã§ã®æ¯è¼ã«ã失æãã¾ãã
func N[T any](n T) {} func N1[T any](n T) func(T) { return N[T] } func N2[T any](n T) func(T) { return N[T] } func main() { fmt.Println(reflect.ValueOf(N[int]).Pointer() == reflect.ValueOf(N[int]).Pointer()) // true fmt.Println(reflect.ValueOf(N1[int]).Pointer() == reflect.ValueOf(N[int]).Pointer()) // false fmt.Println(reflect.ValueOf(N2[int]).Pointer() == reflect.ValueOf(N2[int]).Pointer()) // true }
runtime.FuncForPC ãªã©ãå«ãã¦è²ã ã¨è©¦ãã¦ã¿ãããã©ãGo 1.22æç¹ã§ã¯è¯ãæ¹æ³ããªãã£ãã®ã§ãä»ã®å½¢ã«è½ã¡çãã¾ããã