使用接口模拟数据库返回类型

时间:2017-08-09 17:03:36

标签: go mocking

如何使用接口正确模拟某些*sql.DB方法进行单元测试?例如,我想要一个Store结构,用作我的处理程序的数据源。它有一个conn字段*sql.DB。但是在我的测试中我想模拟数据库,所以我尝试不返回实际的*sql.Rows,而是返回*sql.Rows应该满足的接口。这是代码:

type TestRows interface {
    Scan ()
}

type TestDB interface {
    Query (query string, args ...interface{}) (TestRows, error) 
}

type Rows struct {
    //..
}

func (r *Rows) Scan () {
    fmt.Println("Scanning")
}

这是*sql.DB

type DB struct {
    //...
}

func (d *DB) Query (query string, args ...inteface{}) (*Rows, error) {
    return &Rows{/* .... */}, nil
}

当我想要实例化Store时,它会抛出错误

type Store struct {
    conn TestDB
}

func main() { 
    cl := Store {conn: &DB{}}
    fmt.Println("Hello, playground")
}

cannot use DB literal (type *DB) as type TestDB in field value:
    *DB does not implement TestDB (wrong type for Query method)
        have Query(string) (*Rows, error)
        want Query(string) (TestRows, error)

2 个答案:

答案 0 :(得分:1)

我建议嘲笑数据库是一个坏主意。您可以验证是否使用特定的SQL字符串调用查询并返回虚假结果,但这确实没用。这些测试增加了很少的价值并且增加了一种虚假的自信心。测试接受的内容与数据库中实际运行的内容之间绝对没有联系。

真正的危险之一是您的数据库架构可能会发生变化,但您的模拟不会更新。然后你的代码将在生产中失败但通过测试。

相反,实现某种数据抽象层,例如,像Data Access Object(DAO)。

您可以编写自动集成测试,以针对您的数据库测试DAO。这将确保您的DAO实际工作并捕获任何错误,例如在架构更改后未更新的查询。

然后,您可以为其他代码编写单元测试。任何与数据库交互的东西都应该使用mock / stub / fake DAO,具体取决于测试所需的内容。

答案 1 :(得分:0)

Query函数的定义必须是:

func (d *DB) Query(query string, args ...interface{}) (TestRows, error) {
    return &Rows{count: 1}, nil
}

编辑:

如果 - 你指出 - 你不能改变Query功能的实现,因为你需要遇到其他接口,那么你需要调整自己的接口。在这种情况下

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