26
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

go-sqlmockを使ってGORMで書いたコードをテストする

gopher.png

gormを使ったコードのテストをするとき、Dockerなどを使ってDBを立ち上げてテストする必要がありますが、go-sqlmockを使うと、実際のDBの代わりにモックを使ってテストすることができます。

この記事は、簡単なモデルを例にしたサンプルコードです。

パッケージのインストール

gormとgo-sqlmockは以下のコマンドでインストールできます。

go get -u github.com/jinzhu/gorm
go get -u github.com/DATA-DOG/go-sqlmock

テスト対象のソース

RepositoryパターンでCreateとGetをGormで実装した例です。

user/user.go
package user

import (
    "github.com/jinzhu/gorm"
)

type User struct {
    ID   string `gorm:"primary_key"`
    Name string
}

type Repository struct {
    *gorm.DB
}

func (p *Repository) Create(id string, name string) error {
    person := &User{
        ID:   id,
        Name: name,
    }
    return p.DB.Create(person).Error
}

func (p *Repository) Get(id string) (*User, error) {
    var person User

    err := p.DB.Where("id = ?", id).Find(&person).Error
    return &person, err
}

テストコード

先程のコードの、CreateとGetをそれぞれテストしてみます。

まず、DBモックとGORMのオープンです。

user/user_test.go
package user

import (
    "regexp"
    "testing"

    sqlmock "github.com/DATA-DOG/go-sqlmock"
    "github.com/jinzhu/gorm"
)

func getDBMock() (*gorm.DB, sqlmock.Sqlmock, error) {
    db, mock, err := sqlmock.New()
    if err != nil {
        return nil, nil, err
    }

    gdb, err := gorm.Open("postgres", db)
    if err != nil {
        return nil, nil, err
    }
    return gdb, mock, nil
}

これを使ってテストを書いてみます。

Createのテスト

user/user_test.go
func TestCreate(t *testing.T) {
    db, mock, err := getDBMock()
    if err != nil {
        t.Fatal(err)
    }
    defer db.Close()
    db.LogMode(true)

    r := Repository{DB: db}

    id := "2222"
    name := "BBBB"

    // Mock設定
    mock.ExpectQuery(regexp.QuoteMeta(
        `INSERT INTO "users" ("id","name") VALUES ($1,$2)
         RETURNING "users"."id"`)).
        WithArgs(id, name).
        WillReturnRows(
            sqlmock.NewRows([]string{"id"}).AddRow(id))

    // 実行
    err = r.Create(id, name)
    if err != nil {
        t.Fatal(err)
    }
}

mock.ExpectQueryのところで、期待するSQLとパラメータと、実行結果のレコードを設定しています。
実際に実行した時に、この期待結果と異なる場合、以下のようなエラーが返ります。

--- FAIL: TestCreate (0.00s)
    user_test.go:48: Query 'INSERT INTO "users" ("id","name") VALUES ($1,$2) RETURNING "users"."id"', arguments do not match: argument 0 expected [string - 2222X] does not match actual [string - 2222]

Getのテスト

user/user_test.go
func TestGet(t *testing.T) {
    db, mock, err := getDBMock()
    if err != nil {
        t.Fatal(err)
    }
    defer db.Close()
    db.LogMode(true)

    r := Repository{DB: db}

    id := "1111"
    name := "AAAA"

    // Mock設定
    mock.ExpectQuery(regexp.QuoteMeta(
        `SELECT * FROM "users" WHERE (id = $1)`)).
        WithArgs(id).
        WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).
            AddRow(id, name))

    // 実行
    res, err := r.Get(id)
    if err != nil {
        t.Fatal(err)
    }

    if res.ID != id || res.Name != name {
        t.Errorf("取得結果不一致  %+v", res)
    }
}
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
26
Help us understand the problem. What are the problem?