使用数据库断言

我们已经学习了如何在测试环境中启动应用程序,以及如何为应用编写和运行 E2E 测试。这使我们能够验证应用程序的行为,但我们如何确保存储的数据和数据库组件是正确的呢?E2E 测试的最后一个方面将帮助我们回答这个问题——数据库测试。回顾我们迄今为止编写的测试,我们注意到两点:

  • 数据库通常初始化为空,然后在应用程序关闭时销毁表格。这样做的好处是我们可以确保测试时没有任何持久化数据会干扰测试,但它的缺点是必须在测试过程中设置任何所需的数据。例如,在我们的案例中,注册一本可用书籍需要一个用户 ID,因此我们必须先创建一个用户,才能进行与书籍相关的操作。这可能会导致测试套件的运行时间变长。

  • 数据库中的项目是通过 BookSwap 端点进行断言的。例如,我们通过向 GET /users/{id} 发出请求来检查用户是否正确地存储在数据库中。然而,由于请求会经过整个应用程序栈,这使得很难确定错误的来源。

接下来,我们将进一步探讨这两个痛点,以更好地理解如何解决它们。

种子数据

如前所述,在持久化存储部分,我们使用 golang-migrate 来编写数据库迁移,这使我们能够在应用程序启动之前创建和设置我们的数据库。然后,我们使用 GORM 库作为 ORM,这使我们可以轻松地使用自定义类型与数据库进行交互。

下一步是将一些数据插入我们新创建的表中。这类初始数据被称为种子数据(seed data)。我们可以通过简单地在迁移文件中添加相应的 SQL 语句来将种子数据添加到数据库中。例如,我们可以在 up 迁移中使用 INSERT 命令在表格创建后插入一个初始用户:

INSERT INTO users VALUES ('ABC-123','Initial user', '1 London Road', 'N1', 'UK');
sql

然而,迄今为止我们探索的工具并没有创建和添加随机种子数据的功能。我们本可以添加一个额外的库依赖来生成随机数据,但我们可以利用 GORMDB 类型,在测试运行之前向数据库中插入随机数据:

func addUser() error {
    dbConn, err := gorm.Open(postgres.Open(postgresURL), &gorm.Config{})
    if err != nil {
        return err
    }
    dbConn.Save(&db.User{
        ID:   uuid.New().String(),
        Name: "Generated User",
    })
    return nil
}
go

上面的代码片段演示了如何在运行 Godog 测试步骤时将数据插入数据库,具体步骤如下:

  1. 就像在应用程序启动时一样,我们打开与 PostgreSQL 数据库的连接。数据库连接应该尽可能在多个测试之间共享,而不应为每次测试迭代打开新的连接。然而,由于 E2E 测试通常需要初始化数据库,因此以这种方式设置数据库是可行的。

  2. 一旦数据库连接成功打开,我们使用 GORM 数据库的 Save 方法保存一个生成的用户。之后,数据库将包含生成的数据,并可以在我们的测试中使用。

测试用例和断言

在对数据库内容进行断言时,我们可以采用与添加生成数据时相同的方法:

func verifyUser(want db.User) error {
    dbConn, err := gorm.Open(postgres.Open(postgresURL), &gorm.Config{})
    if err != nil {
        log.Fatal(err)
    }
    var got db.User
    if err := dbConn.Where("id = ?", want.ID).First(&got); err != nil {
        return err
    }
    if want != got {
        return fmt.Errorf("user does not match: got %v, want %v", got, want)
    }
    return nil
}
go

从验证代码中,我们可以看到相同的方法:

  1. 我们使用连接字符串连接到数据库。当多个 goroutine 使用数据库连接时,GORM 会优化数据库连接的使用。

  2. 然后,我们使用数据库方法查询传递给方法的用户 ID。请注意,我们直接依赖 GORM 数据库,而不是我们自己的 UserService 方法,这样可以消除可能引入的错误。

虽然 GORM 使用起来很方便,但编写完整的数据库查询来对返回值进行断言可能会显得有些冗长。dbassert 这个开源库( https://github.com/hashicorp/dbassert )提供了封装和辅助函数,可以使这个过程变得更加简洁。你可以自行探索这个库,看看它如何帮助简化你的测试代码。