为什么你写的代码总是有 Bug?用它来保证 Go 代码质量

序幕
这篇文章是集成测试系列两个部分中的第二部分 。你可以先读 Go 语言中的集成测试:第一部分 - 用 Docker 执行测试 。本文中的示例可以从 代码仓库[1] 获取 。
简介

“比起测试行为,设计测试行为是已知的最好的错误预防程序之一 。” —— Boris Beizer
在执行集成测试之前,必须正确配置该测试相关的外部系统 。否则,测试结果是无效和不可靠的 。例如,数据库需要有定义好的数据,这些数据对于要测试的行为是正确的 。测试期间更改的数据需要进行验证,尤其是如果要求更改的数据对于后续测试而言是准确的时侯 。
Go 测试工具提供了有在执行测试函数前执行代码的能力,使用叫做 TestMain 的入口函数实现 。它类似于 Go 应用程序的 Main 函数 。有了 TestMain 函数,我们可以在执行测试之前做其他系统配置,比如数据库连接之类的 。在本文中,我将分享如何使用它 TestMain 来配置和连接 Postgres 数据库,以及如何针对该数据库编写和运行测试 。
填充初始数据为了填充数据库,需要定义数据并将其放置在测试工具可以访问的位置 。一种常见的方法是定义一个 SQL 文件,该文件是项目的一部分,并且包含所有需要执行的 SQL 命令 。另一种方法是将 SQL 命令存储在代码内部的常量中 。不同于这两种方法,我将只使用 Go 语言实现来解决此问题 。
通常情况下,你已将你的数据结构定义为 Go 结构体类型,用于数据库通信 。我将利用这些已存在的数据结构,已经可以控制数据从数据库中流入流出 。基于已有的数据结构声明变量,构造所有填充数据,而无需 SQL 语句 。
我喜欢这种解决方式,因为它简化了编写集成测试和验证数据是否能够正确用于数据库和应用程序之间的通信的 。不必将数据直接与 JSON 比较,就可以将数据解编为适当的类型,然后直接与为之前数据结构定义的变量进行比较 。这不仅可以最大程度地减少测试中的语法比较错误,还可以使您的测试更具可维护性、可扩展性和可读性 。
填充数据库
译者注:原文为 Seeding The Database,下面部分相关功能函数就称为种子函数
本文提到的,所有用于填充数数据库功能函数,都在 `testdb`[2] 包中 。这个包仅用于测试,不用做第三方依赖 。用来辅助填充测试数据库的三个核心函数分别是:SeedLists, SeedItems, 和Truncate,如下:
这是 SeedLists 函数:
代码清单 1func SeedLists(dbc *sqlx.DB) ([]list.List, error) {    now := time.Now().Truncate(time.Microsecond)    lists := []list.List{        {            Name:     "Grocery",            Created:  now,            Modified: now,        },        {            Name:     "To-do",            Created:  now,            Modified: now,        },        {            Name:     "Employees",            Created:  now,            Modified: now,        },    }    for i := range lists {        stmt, err := dbc.Prepare("INSERT INTO list (name, created, modified) VALUES ($1, $2, $3) RETURNING list_id;")        if err != nil {            return nil, errors.Wrap(err, "prepare list insertion")        }        row := stmt.QueryRow(lists[i].Name, lists[i].Created, lists[i].Modified)        if err = row.Scan(&lists[i].ID); err != nil {            if err := stmt.Close(); err != nil {                return nil, errors.Wrap(err, "close psql statement")            }            return nil, errors.Wrap(err, "capture list id")        }        if err := stmt.Close(); err != nil {            return nil, errors.Wrap(err, "close psql statement")        }    }    return lists, nil}


推荐阅读