やる気がストロングZERO

やる気のストロングスタイル

【Go】インターフェースから具象型への型アサーション

インターフェースに依存させてコードを書いてると、テストで具象型として扱いたいケースが出てきたので方法を調べた。

参考)
A Tour of Go

value := <インターフェース型の変数>.(<具象型>)
value, ok := <インターフェース型の変数>.(<具象型>) // この場合okには成否が入る。

呼び方がいまいちわかってないが、

  • [具象型 => 別の具象型(intからfloatとか)]:キャスト
  • [インターフェース => 具象型]:型アサーション

という感じなのかな?

サンプルケース

Human構造体は言葉を覚えるMemoriseメソッドを持っている。

type Human struct {
}
func (h *Human) Memorise(word string) {
   // 覚える処理
}

本番運用時はDBに言葉を記録するが、テスト時はインメモリに保存したいので、
以下のようにPersistenceインターフェースを用意して、DIで切り替えられるようにした。

type Human struct {
   persistence Persistence
}
func (h *Human) Memorise(word string) {
   h.persistence.Save(word)
}

// インターフェース
type Persistence interface {
   Save(word string)
}


// Dbに保存するPersistence
type DbPersistence struct {
   // なんかdbに保存するためのプロパティなど
}
func (p *DbPersistence) Save(word string) {
   // dbに保存するための処理
}

// memoryに保存するPersistence テスト用
type MemoryPersistence struct {
   memory []string
}
func (p *MemoryPersistence) Save(word string) {
   p.memory = append(p.memory, word)
}

Memoriseテストの実行時、言葉を覚えているかをチェックするため以下のようにmemoryの内部を確認したい。
こんな感じで型アサーションする。

func Test_SomeTest(t *testing.T) {
   masao := Human{
      persistence: &MemoryPersistence{},
   }
   masao.Memorise("人に厳しく自分に甘く")

   // persisitenceはPersistenceインターフェース型だからmemoryにアクセスできない。コンパイルエラーになる
   if masao.persistence.memory[0] != "人に厳しく自分に甘く"{ 
    t.Errorf("正しく覚えてない")
   }

   // こうやって型アサーションしてMemoryPersistence型として扱えばmemoryにアクセスできる。
   memoryPersistence := masao.persistence.(*MemoryPersistence)
   if memoryPersistence.memory[0] != "人に厳しく自分に甘く" {
      t.Errorf("正しく覚えてない")
   }
}