Bu basit bir veritabanı test fikstürüdür ve Golang test senaryolarınızda kullanabilirsiniz. İhtiyacınız olan tek şey, bir veritabanı örneği oluşturan ve tablo temizleme özelliği olan aşağıdaki küçük paket.


Veritabanı yardımcı paketi


package test

import (
"database/sql"
"fmt"
)

func OpenDB(driver, address string, maxIdleConns int) *sql.DB {
db, _ := sql.Open(driver, address)
db.SetMaxIdleConns(maxIdleConns)

return db
}

func CloseDB(db *sql.DB) {
_ = db.Close()
}

func TruncateTables(db *sql.DB, tables []string) {
_, _ = db.Exec("SET FOREIGN_KEY_CHECKS=0;")

for _, v := range tables {
_, _ = db.Exec(fmt.Sprintf("TRUNCATE TABLE %s;", v))
}

_, _ = db.Exec("SET FOREIGN_KEY_CHECKS=1;")
}

Test


Elimizde leagues isminde bir veritabanı olduğunu varsayalım. Öncelikle içindeki tabloları temizliyoruz. Daha sonra testler için gerekli olan veriler ile donatıyoruz. Temel prensip aşağıdaki gibidir.


  1. Bir veritabanı örneği alınır.

  2. leagues tablosu temizlenir.

  3. leagues tablosu test verileriyle doldurulur.

  4. Her testten önce loadFixtures fonksiyonu çağrılır.

Yardımcı dosya


Bu ilk üç adım içindir.


package league

import (
"database/sql"
"fmt"
"os"
"strings"
"testing"

"internal/pkg/test"
)

// -----------------------------------------------------------------------------

var DB *sql.DB

func TestMain(m *testing.M) {
setup()
code := m.Run()
teardown()
os.Exit(code)
}

func setup() {
// Prepare database.
dbAdr := "user:pass@tcp(0.0.0.0:3306)/football?charset=utf8mb4&collation=utf8mb4_unicode_ci"
dbDrv := "mysql"
dbIdl := 5

// Obtain a database instance.
DB = test.OpenDB(dbDrv, dbAdr, dbIdl)

fmt.Printf("\033[1;36m%s\033[0m", "> Setup completed\n")
}

func teardown() {
// Close the database instance.
test.CloseDB(DB)

fmt.Printf("\033[1;36m%s\033[0m", "> Teardown completed")
fmt.Printf("\n")
}

// -----------------------------------------------------------------------------

func loadFixtures(db *sql.DB) {
test.TruncateTables(db, []string{"leagues"})
fmt.Printf("\033[1;36m%s\033[0m", "> Tables truncated\n")

data := []struct {
name interface{}
isActive interface{}
createdAt interface{}
deletedAt interface{}
}{
{
"La Liga",
"true",
"2019-12-31 23:59:59",
"null",
},
{
"Premier League",
"true",
"2019-12-31 23:59:59",
"null",
},
}

query := `
INSERT INTO leagues
(name, is_active, created_at, deleted_at)
VALUES
`

for _, d := range data {
query += fmt.Sprintf(
"('%v', %v, '%v', %v),\n",
d.name,
d.isActive,
d.createdAt,
d.deletedAt,
)
}
query = strings.TrimSuffix(query, ",\n")

_, _ = db.Exec(query)

fmt.Printf("\033[1;36m%s\033[0m", "> Fixtures loaded\n")
}

Önemli not


Testlerinize bağlı olarak, büyük olasılıkla Error 1062: Duplicate entry '1' for key 'PRIMARY' benzer bir hata alırsınız. Bu başınıza gelsede gelmesede test.TruncateTables() satırından hemen önce LOCK TABLES {your_table_name} WRITE; ve hemen sonra UNLOCK TABLES; komutlarını çalıştırmalısınız.


Test dosyası


Bu son adım için yani testlerin loadFixtures fonksiyonunu kullandığı yerdir.


package league

import (
"net/http"
"net/http/httptest"
"testing"
)

func TestCreate(t *testing.T) {
// DB is already ready for you!
loadFixtures(DB)

rq := httptest.NewRequest(http.MethodGet, "/leagues", nil)
rw := httptest.NewRecorder()

createHandler := NewCreate(DB)
createHandler.Create(rw, rq)

// Assert against rw.Body.String()
}

Sonuç


Aşağıda görebileceğiniz gibi, sadece TestCreate test senaryomuz veri fikstürlerine bağlıdır, diğerleri değil.


$ go test ./... -v

> Setup completed
=== RUN TestRetrieve
--- PASS: TestRetrieve (0.00s)
> Tables truncated
> Fixtures loaded
=== RUN TestCreate
--- PASS: TestCreate (0.00s)
=== RUN TestList
--- PASS: TestList (0.00s)
PASS
> Teardown completed
ok internal/league 0.051s