我一直在努力研究单元测试、依赖注入(inject)、tdd 和所有这些东西,例如,我一直专注于测试进行数据库调用的函数。
假设您有一个 PostgresStore 结构,它接受一个数据库接口(interface),该接口(interface)具有 Query() 方法。
type PostgresStore struct {
db Database
}
type Database interface {
Query(query string, args ...interface{}) (*sql.Rows, error)
}
并且您的 PostgresStore 有一个 GetPatients 方法,该方法调用数据库查询。
func (p *PostgresStore) GetPatients() ([]Patient, error) {
rows, err := p.db.Query("SELECT id, name, age, insurance FROM patients")
if err != nil {
return nil, err
}
defer rows.Close()
items := []Patient{}
for rows.Next() {
var i Patient
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Surname,
&i.Age,
&i.InsuranceCompany,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
在真正的实现中,您只需传递 *sql.DB 作为数据库参数,但是你们如何使用假数据库结构编写单元测试?
最佳答案
让我尝试澄清您的一些疑虑。首先,我将分享一个工作示例,以更好地理解正在发生的事情。然后,我将提及所有相关方面。
repo/db.go
package repo
import "database/sql"
type Patient struct {
ID int
Name string
Surname string
Age int
InsuranceCompany string
}
type PostgresStore struct {
// rely on the generic DB provided by the "sql" package
db *sql.DB
}
func (p *PostgresStore) GetPatient(id int) ([]Patient, error) {
rows, err := p.db.Query("SELECT id, name, age, insurance FROM patients")
if err != nil {
return nil, err
}
defer rows.Close()
items := []Patient{}
for rows.Next() {
var i Patient
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Surname,
&i.Age,
&i.InsuranceCompany,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
这里,唯一相关的更改是如何定义 PostgresStore
结构。作为db
字段,您应该依赖Go标准库的database/sql
包提供的通用DB
。正因为如此,用假的实现替换它的实现是微不足道的,我们稍后会看到。
Please note that in the
GetPatient
method you're accepting anid
parameter but you're not using it. Your query is more suitable to a method likeGetAllPatients
or something like that. Be sure to fix it accordingly.
repo/db_test.go
package repo
import (
"testing"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
)
func TestGetPatient(t *testing.T) {
// 1. set up fake db and mock
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("err not expected: %v", err)
}
// 2. configure the mock. What we expect (query or command)? The outcome (error vs no error).
rows := sqlmock.NewRows([]string{"id", "name", "surname", "age", "insurance"}).AddRow(1, "john", "doe", 23, "insurance-test")
mock.ExpectQuery("SELECT id, name, age, insurance FROM patients").WillReturnRows(rows)
// 3. instantiate the PostgresStore with the fake db
sut := &PostgresStore{
db: db,
}
// 4. invoke the action we've to test
got, err := sut.GetPatient(1)
// 5. assert the result
assert.Nil(t, err)
assert.Contains(t, got, Patient{1, "john", "doe", 23, "insurance-test"})
}
这里有很多内容需要介绍。首先,您可以检查代码中的注释,以便您更好地了解每个步骤。在代码中,我们依赖于 github.com/DATA-DOG/go-sqlmock
包,它允许我们轻松模拟数据库客户端。
显然,这段代码的目的是给出如何实现您的需求的总体思路。它可以用更好的方式编写,但它可以成为在此场景中编写测试的良好起点。
请告诉我这是否有帮助,谢谢!
关于database - 在没有库的情况下如何模拟数据库调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75373245/