2017-10-22 4 views
0

TLDR:データベースをデータソースとして使用するが、データソースがデータベースである必要がない(つまり、データがどこに来るのかを認識していない)関数をユニットテストする最良の方法は何ですか?それはオブジェクトなどである可能性があります)?GoLangにデータベース非依存関数を書き込んで単体テストを容易にする方法は?

= - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - === = = = = = = =

データベースをデータソースとして使用する単体テスト機能のベストプラクティスを見つけようとしています。パラメタ - しかし、データソースはデータベースである必要はありません(例えば、オブジェクトなど)。データソースがデータベースである必要はないという句を追加する理由は、単体テスト中にデータベースを使用したくないからです。では、データベースに依存しない関数を作成するにはどうすればよいですか?

1つの可能なアプローチは、そう(GoLang)のようなデータソースを保持している「ENV」パラメータを提供することである。

type Env struct { 
    DataSource interface{} 
} 

func FunctionToTest(env Env) { 
    switch et := (env.DataSource).(type) { 
    case UserDatasource: 
     userSource := (env.DataSource).(UserDatasource) 
     user := userSource.getUser() 
    default: 
     // Throw error 
    } 
} 

func FunctionToTest2(env Env) { 
    switch et := (env.DataSource).(type) { 
    case CredentialsDatasource: 
     credentialSource := (env.DataSource).(CredentialsDatasource) 
     password := credentialSource.getPassword() 
    default: 
     // Throw error 
    } 
} 

これで問題は、それが「ハック」ようで、そこに存在するようにそれは感じているということですよりよい解決策です。私がこれを行う方法を学びたい理由は、データベースを模擬する単体テストを書くことができるようにするためです。

私はすべての入力を感謝します。

ありがとうございます!

+0

をテストするための

package mocks type UserRepository struct { Users []*user.User ShouldError bool } func (r *UserRepository) Find(id int64) (*user.User, error) { if r.ShouldError { return nil, errors.New("") } for _, u := range r.Users { if u.ID == id { return u } } return errors.New("user not found") } func (r *UserRepository) Store(u *user.User) error { if r.ShouldError { return errors.New("") } r.Users = append(r.Users, u) return nil } 

。つまり、引数を持たず、ユーザーや資格情報など、必要なリソースタイプを返す関数です。 –

答えて

0

ここではインターフェイスを使用します。データがどこに由来するかを抽象化するために使用される一般的なパターンは、リポジトリパターンです。 Hereは、よく書かれたGoアプリケーションを開発するためのリポジトリパターンやその他のヒントについて読むことができる記事です。

package user 

type User struct { 
    ID int64 
    Name string 
} 

type Repository interface { 
    Find(id int64) (*User, error) 
    Store(u *User) error 
} 

func ChangeUserName(id int64, name string, r Repository) error { 
    u, err := r.Find(id) 
    if err != nil { 
     return err 
    } 

    u.Name = name 
    err = r.Store(u) 
    return err 
} 

user.Repositoryインターフェイスに一致するメソッドで構造体を渡すことができます。たとえば:その後、

package mysql 

type DB struct { 
    *sql.DB 
} 

func New(db *sql.DB) *DB { 
    return DB{db} 
} 

func (d *DB) Find(id int64) (*user.User, error) { 
    // mysql stuff to find user 
} 

func (d *DB) Store(u *user.User) error { 
    // mysql stuff to store user 
} 

...

package main 

func main() { 
    // ... 
    // conn := code to open *sql.DB connection 
    db := mysql.New(conn) 
    err := user.ChangeUserName(1, 'bob', db) 
    // ... 
} 

あなたは今もあなたの機能をテストするためにモックを使用することができます。そしておそらく、関数は、引数として供給機能を受け取る必要があります...

func Test_ChangeUserName_Stores_Changed_User(t *testing.T) { 
    u := &user.User{ID: 1, Name: 'Bob'} 
    r := mocks.UserRepository{Users: []*user.User{u}} 
    err := user.ChangeUserName(1, 'Fred', r) 
    // ... 
} 
関連する問題