手軽にgo1.6を試す方法考える -> 結局dockerを使うことにした

はじめに

期待していない挙動を示した時にそもそもgo1.7とgo1.6とで動作が異なるのではないか?という疑いをもった時があった。 ところで手元の環境は全部1.7でわざわざ1.6をbuildするのも面倒と言うような感じ。 本当に些細なコードだったので、goの1.6用のplaygroundがあればそちらを使いたかったのだけれど。みつけられなかった。

特にvmや他の環境を持っていない場合にどうするのが一番楽なんだろう?という話。

dockerを使うことにした

dockerを使うことが一番ラクかもしれない。以下にdocker用のイメージがある。alpineがたぶんいちばん小さいだろうということでそれを使うことにする。

以下のようなdocker-compose.ymlを書いてあげる。

go1.6:
  image: golang:1.6-alpine
  volumes:
    - "$PWD:/go"

例えば以下の様なコードを実行してみる。

dump-info.go

package main

import (
    "fmt"
    "os"
    "runtime"
)

func main() {
    fmt.Printf("go version: %q\n", runtime.Version())
    pwd, _ := os.Getwd()
    fmt.Printf("cwd: %q\n", pwd)
}

以下の様にして実行できる。

$ docker-compose run go1.6 go run dump-info.go
go version: "go1.6.3"
cwd: "/go"

1.6で動いている。

参考

既存のmethodを壊さずに内部のmethodを差し替えて実行したい

はじめに

例えば、以下のような状況で M.F0() を呼びたい。しかしその内部で呼ばれる m.f()は呼んでほしくない場合。

type M struct {
}

func (m *M) F0() error {
    // do something
    return m.f()
}

func (m *M) F1() error {
    // do something
    return m.f()
}

func (m *M) f() error {
    // この処理を呼びたくない
    panic("don't call!!1")
}

通常のアプリケーションコードとして結合された状態で呼ばれる分には何ら問題無いコードも、テスト中では呼び出したくない場合がある。 例えば、 M.f() が外部のリソースに依存するような処理だった場合など、テスト中では呼び出したくない。

もちろん現状で以下の呼び出しはpanicする。

m.F0() // panic
m.F1() // panic

この時、公開された関数である M.F0() , M.F1() の内部で呼ばれる M.f() の挙動を差し替えたい。

単純なembedではダメ

以下はどう考えてもダメ。

type mockM struct {
    *M
}

func (m *M) f() error {
    // こちらが呼ばれてくれると嬉しいけれど
}

embedにより書き換えたつもりになっているのかもしれないけれど。mockM.F0() の呼び出しで、実質呼ばれるのは内部の M.F0()。 その為、結局 M.f() の方が呼ばれてしまう。

内部の部分を切り分けてembedするのが無難そう

内部の部分を切り分けてembedするのが無難そう。型定義を以下の様に変える。

type mClient interface {
    f() error
}

type M struct {
    mClient
}

内部で利用するclientのinterfaceを定義。 デフォルトではactualMClientの方を利用し、テストの時などはmockedMClientの方を利用する。

type actualMClient struct {
}

func (c *actualMailerClient) f() error {
    // この処理を呼びたくない
}

type mockedMClient struct {
}

func (c *mockedMClient) f() error {
    // こちらが呼ばれてくれると嬉しい
}

以下のような関数があるとうれしいかも?

func NewM() *M{
    client := actualMClient{}
    m := M{mClient: &client}
    return &m
}