質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

Q&A

1回答

1709閲覧

Goでinterfaceを使ってモックオブジェクトを実装したい

退会済みユーザー

退会済みユーザー

総合スコア0

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

0グッド

0クリップ

投稿2017/12/27 17:14

編集2017/12/27 17:15

私がしたいことは以下のようです。

  • HTTPリクエストを送るメソッドがあるが、testでは時間がかかってしまうためモックオブジェクトを使いたい。
  • その際にinterface型でうまくできるようなのですが、どのようにしたらいいのでしょうか?

以下のようなメソッドをテストにしたいのです。

go

1type client struct {} 2 3func (c *client) Send() bool{ 4 //HTTPリクエストで処理 5 // 真偽値を返す 6}

どのようにしたらいいのでしょうか?

go

1type client interface{ 2 Send() bool 3} 4 5func New() client { 6 return &fakeClient 7} 8 9func (f *fakeClient) Send() bool { 10 // モックなのでただ真偽値を返す 11}

このようにしてあたかも本物みたいに

go

1client := NewFake() 2client.Send()

ぐらいしかないですか?僕としてはあたかも本番コードと同じようにテストを書きたいです。
また、このモックオブジェクトの作り方が正しいとしても

go

1func New() client { 2 return &fakeClient 3}

でinterfaceとして本番コードと同じように書けるくらいのメリットしかありませんよね?
たとえば本番コードが更に複雑なメソッドがあったら、モックオブジェクトも同じように実装できるか不安で「使うメリットがあるのか」と思ってしまいます。

どのようにすればいいテストがかけるでしょうか?
HTTPリクエストだけでなく、もっと処理に時間がかかるテストがモックオブジェクトで書きたいのです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

以下は一例です。client に動作が変えられる物をフィールドとして持たせておき、テストの場合だけそれを差し替える事でテスト出来ます。例えば以下はサーバからの応答が OK である事を期待するコードです。

go

1package main 2 3import ( 4 "io/ioutil" 5 "net/http" 6) 7 8type client struct { 9 uri string 10} 11 12func NewClient() *client { 13 return &client{ 14 uri: "http://mock.example.com/api", 15 } 16} 17 18func (c *client) Send() bool { 19 resp, err := http.Get(c.uri) 20 if err != nil { 21 return false 22 } 23 defer resp.Body.Close() 24 b, err := ioutil.ReadAll(resp.Body) 25 if err != nil { 26 return false 27 } 28 return string(b) == "OK" 29} 30 31func DoSend(c *client) bool { 32 return c.Send() 33} 34 35func main() { 36 client := NewClient() 37 DoSend(client) 38}

この DoSend() がテストしたいとします。

go

1package main 2 3import ( 4 "net/http" 5 "net/http/httptest" 6 "testing" 7) 8 9func TestMock(t *testing.T) { 10 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 11 if r.URL.Path == "/ok" { 12 w.Write([]byte("OK")) 13 return 14 } 15 if r.URL.Path == "/ng" { 16 w.Write([]byte("NG")) 17 return 18 } 19 })) 20 21 c := NewClient() 22 23 c.uri = ts.URL + "/ok" 24 want := true 25 got := DoSend(c) 26 if got != want { 27 t.Fatalf("want %v but got %v", want, got) 28 } 29 30 c.uri = ts.URL + "/ng" 31 want = false 32 got = DoSend(c) 33 if got != want { 34 t.Fatalf("want %v but got %v", want, got) 35 } 36}

この様に client がアクセスするターゲットを差し替える事で本番コードを変更せずにテスト出来ます。

投稿2017/12/28 06:18

mattn

総合スコア5030

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/12/28 07:24

すみません、もう少し説明していただけませんか、、、初心者でテストを頑張ろうとしていて、、、すみません、、
mattn

2017/12/29 14:41

どのあたりが分からないか書いてもらえると説明しやすそうです。 今回の質問者さんのコードだと、テストしたいのは client の動きであって、クライアントの作成部分 New ではないはずです。であれば client interface を実装した FakeClient を作って渡すまでがテストの範囲になりますよね。 逆に client の作成部分 New も含めてテストしたいという事であれば、FakeClient を渡す事が出来ないので、代わりに本物の client を使いつつ動きを変えられる様な仕組みを入れる必要があります。 今回の僕のコードだと本番の URL をテストコードから差し替える事で、テスト時だけ異なる動作をさせています。 要はテストしやすい様に処理毎に関数分けされていれば、モックであったり、僕の例の様な方法であったり、色んな方法でインジェクト出来るはずという事です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問