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

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

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

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

テスト駆動開発

テスト駆動開発は、 プログラム開発手法の一種で、 プログラムに必要な各機能をテストとして書き、 そのテストが動作する必要最低限な実装を行い コードを洗練させる、といったサイクルを繰り返す手法の事です。

Q&A

解決済

3回答

3897閲覧

Golangで単体テストコードの実装方法

kappazushi

総合スコア13

Go

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

テスト駆動開発

テスト駆動開発は、 プログラム開発手法の一種で、 プログラムに必要な各機能をテストとして書き、 そのテストが動作する必要最低限な実装を行い コードを洗練させる、といったサイクルを繰り返す手法の事です。

2グッド

1クリップ

投稿2016/05/01 03:36

###前提・実現したいこと
Golangである基本的なメソッドの単体テストを実装しようとしています。

###発生している問題・エラーメッセージ
しかし、データ型をどのように解決すればよいのかがわかりません。
下に示したのはAppのクラスの中で、Appのフィールドの一つにDaoを入れています。そして、Appのメソッド(A)の中ででDaoのメソッド(D)を呼び出しています。そのAppのメソッド(A)の単体テストがしたいです。しかし、そのAppのメソッド(A)の中で呼び出しているDaoのメソッド(D)のテストは不要ですので、そのDaoのメソッド(D)をtestではダミーのメソッドにしたいです。

###該当のソースコード

package main import ( "fmt" ) // Dao type Dao struct{} func (this *Dao) D() { fmt.Println("D") } // App type App struct { dao *Dao } func (this *App) SetDao(dao *Dao) { this.dao = dao } func (this *App) A() { this.dao.D() } func main() { app := &App{dao: &Dao{}} app.A() // D が返ってくるが、別の戻り値にしたい }

###試したこと
インターフェイスでの実装しました。ですが、Daoのメソッドはたくさんあるので、一つの単体テストのたびに、Daoのmethod全てを実装したDummyクラスを作る必要が生まれてしまうのでダメでした。

package main import ( "fmt" ) // Dao type Dao struct{} func (this *Dao) D() { fmt.Println("D") } // App type App struct { // dao *Dao dao Der } func (this *App) SetDao(dao *Dao) { this.dao = dao } func (this *App) A() { this.dao.D() } // D&Dummy Interface type Der interface { D() } // dummy type Dummy struct{} func (this *Dummy) D() { fmt.Println("Dummy") } func main() { app := &App{dao: &Dao{}} app.A() // D app2 := &App{dao: &Dummy{}} app2.A() // Dummy }

本当はJavaScriptの用に、あるクラスのメソッドをインスタン後に無名関数などでreplaceできれば簡単にdummyメソッドを追加できるのですが、Golangでは何かいい方法はないでしょうか?

###補足情報(言語/FW/ツール等のバージョンなど)
golang 1.6

raccy, fagai👍を押しています

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

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

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

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

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

guest

回答3

0

Dao を interface にするのは同じですが、基本の BaseDao 型を作っておいて、テストで任意のメソッドをすげ替えたい場合は BaseDao を埋め込んだダミー型を宣言して、すげ替えたいメソッドだけ実装し、その他のメソッドは BaseDao に委譲するというのはどうでしょうか?

以下の実装例では説明とテストの都合のため、メソッドを増やしたり返り値もたせたりしています。

app.go

go

1package app 2 3//DAO 4 5type Dao interface { 6 D() string 7 D2() string 8} 9 10type BaseDao struct {} 11 12func (b *BaseDao) D() string { 13 return "D" 14} 15 16func (b *BaseDao) D2() string { 17 return "D2" 18} 19 20//APP 21 22type App struct { 23 dao Dao 24} 25 26func (a *App) SetDao(d Dao) { 27 a.dao = d 28} 29 30func (a *App) A() string { 31 return a.dao.D() 32} 33 34func (a *App) A2() string { 35 return a.dao.D2() 36}

app_test.go

go

1package app 2 3import "testing" 4 5//TEST BaseDao 6 7func TestApp_A(t *testing.T) { 8 app := &App{} 9 dao := &BaseDao{} 10 11 app.SetDao(dao) 12 13 if app.A() != "D" { 14 t.Error("invalid dao") 15 } 16 17 if app.A2() != "D2" { 18 t.Error("invalid dao") 19 } 20} 21 22//TEST Dummy 23 24type DummyDao struct { 25 BaseDao 26} 27 28func (d *DummyDao) D() string { 29 return "Dummy" 30} 31 32func TestApp_A2(t *testing.T) { 33 app := &App{} 34 dao := &DummyDao{} 35 36 app.SetDao(dao) 37 38 if dao.D() != "Dummy" { 39 t.Error("invalid dao") 40 } 41 42 if dao.D2() != "D2" { 43 t.Error("invalid dao") 44 } 45}

投稿2016/05/02 20:22

nirasan

総合スコア19

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

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

0

自己解決

nirasanさんとほとんど同じなのですが、以下の用にしました。

インターフェイス(Boarder)を定義しておき、それをテストのmock用の構造体に埋め込みます。
インターフェイスなのでDaoに必要な仮想メソッドが定義されており、AppのDaoの型チェックは通ります。
そして、テストで実際に必要なメソッドのみ実際にmockのメソッドとして実装します。
基底クラス(構造体)の埋め込みは不要なフィールドがついて来そうなのでやめました。

type mock1 struct { dao.Boarder } func (mock1) AAA(_ string) (bool, error) { return true, nil } func (mock1) BBB(_ string) error { return nil } func (mock1) CCC(_ string) (string, error) { return "AAA", nil } func Test_GetBoardIdByBoardName_OK(t *testing.T) { var err error var boardName string thread := NewThread() thread.SetBoardDao(&mock1{}) boardName, err = thread.getBoardIdByBoardName("") if err != nil { t.Error(err.Error()) } if boardName != "AAA" { t.Error("Board Name Must be AAA") } }

投稿2016/05/05 06:59

編集2016/05/05 07:33
kappazushi

総合スコア13

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

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

0

golang 1.5時代の知識で語ることを最初に断っておきます。

Goは型付けが厳密な言語なので、スタブ化に少し工夫が必要です。書かれているコードだと、Appが完全にDaoに依存しているため、このままではdummyにとばせません。
Daoを定義する際に、あらかじめテストの事を考えて、スタブ化したいメソッドを関数オブジェクトとしてメンバー変数に持つか、グローバル変数に持っておく必要があります。

例えば次の様にして、DaoのメソッドDを、修正することでダミー用関数Eに切り替えることができます。

go

1// Dao 2type Dao struct { 3 D func() // メンバ変数を関数として宣言しておく 4} 5 6//func (this *Dao) D() { Daoのメソッドではなく、普通の関数として定義する 7func D() { 8 fmt.Println("D") 9} 10 11func E() { // 切り替え用の関数 12 fmt.Println("E") 13} 14 15// App 16type App struct { 17 dao *Dao 18} 19 20func (this *App) SetDao(dao *Dao) { 21 this.dao = dao 22} 23 24func (this *App) A() { 25 this.dao.D() 26} 27 28func main() { 29 d := &Dao{} 30 d.D = D // 通常の関数を使用する 31 app := &App{dao: d} 32 app.A() // D が返る 33 34 d.D = E// 切り替え用の関数に切り替え 35 app.A()// Eが返る 36}

E関数やそれをDaoのメンバーに代入している部分をテストコードで記載すれば、テスト時にのみEを呼ぶといったことが可能になります。
また、Daoのメンバーを小文字にしておけば、別パッケージからは見えなくなるので、ある程度は秘匿性も守られると思います。

投稿2016/05/01 12:51

shanxia

総合スコア1038

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問