Go语言 没有库,我怎么模拟数据库调用?

6za6bjd0  于 2023-02-10  发布在  Go
关注(0)|答案(1)|浏览(124)

我一直在尝试把我的头缠在单元测试,依赖注入,tdd和所有的东西上,我一直在测试进行数据库调用的函数,例如。
假设您有一个PostgresStore结构体,它接收一个Database接口,该接口具有一个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作为数据库参数,但是你们怎么用一个假的数据库结构来编写单元测试呢?

nwlqm0z1

nwlqm0z11#

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

一个月一次
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"})
}

这里有很多内容要介绍。首先,你可以查看代码中的注解,这些注解可以让你更好地了解每一步。在代码中,我们依赖于github.com/DATA-DOG/go-sqlmock包,它允许我们轻松地模拟数据库客户端。
显然,这段代码的目的是给出一个关于如何实现你的需求的总体概念。它可以用一种更好的方式编写,但它可以是在这种场景中编写测试的一个很好的起点。
如果有帮助,请告诉我,谢谢!

相关问题