【问题标题】:How can i mock database calls without a library?我如何在没有图书馆的情况下模拟数据库调用?
【发布时间】:2023-02-07 23:42:57
【问题描述】:

我一直在努力研究单元测试、依赖注入、tdd 和所有这些东西,例如,我一直坚持测试进行数据库调用的函数。

假设您有一个接受数据库接口的 PostgresStore 结构,该接口具有 Query() 方法。

type PostgresStore struct {
    db Database
}

type Database interface {
    Query(query string, args ...interface{}) (*sql.Rows, error)
}

你的 PostgresStore 有一个 GetPatients 方法,它调用数据库查询。

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
}

在实际实现中,您只需将 *sql.DB 作为数据库参数传递,但是你们如何使用伪造的数据库结构编写单元测试?

【问题讨论】:

  • AFAICT 你的主要问题是你的Database 接口直接依赖于一种类型,没有简单的方法来提供有效的测试值,即*sql.Rows。所以你要么不应该模拟Database,而是应该实现一个模拟database/sql/driver。或者您需要将接口更改为不依赖于您无法控制的类型,而是依赖于您拥有可能需要的所有控制权的类型(即接口类型)。
  • 嘿伙计,这就是答案。你能给我指出一些资源或 github 项目来实现你刚才解释的内容吗?我很难弄清楚这一点哈哈。谢谢
  • 就个人而言,我不会。 GetPatient 除了与数据库交互之外什么都不做,因此使用模拟数据库对其进行测试的价值有限。我会用模拟 Store 对任何调用 GetPatient 进行单元测试,并且只一体化使用测试 PostgreSQL 数据库测试 PostgresStore 本身。

标签: database go mocking


【解决方案1】:

让我试着澄清你的一些疑虑。首先,我将分享一个工作示例,以更好地理解正在发生的事情。然后,我将提及所有相关方面。

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。多亏了这一点,用一个假的交换它的实现是微不足道的,我们稍后会看到。

请注意,在 GetPatient 方法中,您接受了一个 id 参数,但您没有使用它。您的查询更适合 GetAllPatients 之类的方法。请务必相应地修复它。

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"})
}

在这里,有很多内容要介绍。首先,您可以检查代码中的 cmet,让您更好地了解每个步骤。在代码中,我们依赖包github.com/DATA-DOG/go-sqlmock,它允许我们轻松模拟数据库客户端。

显然,这段代码的目的是给出一个关于如何实现您的需求的总体思路。它可以用更好的方式编写,但它可以作为在此场景中编写测试的良好起点。

让我知道这是否有帮助,谢谢!

【讨论】:

    猜你喜欢
    • 2013-10-20
    • 2013-02-18
    • 2020-10-21
    • 2014-10-24
    • 2023-01-08
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    相关资源
    最近更新 更多