1

我正在尝试在场景之间回滚单元测试中的事务,以保持数据库为空并且不使我的测试变脏。所以,我正在尝试:

for _, test := range tests {
   db := connect()
   _ = db.RunInTransaction(func() error {
      t.Run(test.name, func(t *testing.T) {
         for _, r := range test.objToAdd {
            err := db.PutObj(&r)
            require.NoError(t, err)
         }
         objReturned, err := db.GetObjsWithFieldEqualsXPTO()
         require.NoError(t, err)
         require.Equal(t, test.queryResultSize, len(objReturned))
      })
      return fmt.Errorf("returning error to clean up the database rolling back the transaction")
   })
}

我希望在场景结束时回滚事务,因此下一步将有一个空数据库,但是当我运行时,数据永远不会回滚。

我相信我正在尝试执行文档建议的操作:https://pg.uptrace.dev/faq/#how-to-test-mock-database,对吗?

更多信息:我注意到我的界面在 RunInTransaction 上实现了一个层:

func (gs *DB) RunInTransaction(fn func() error) error {
    f := func(*pg.Tx) error { return fn() }
    return gs.pgDB.RunInTransaction(f)
}

IDK 还有什么问题,但我真的猜想这与此有关(因为 TX 被封装在 RunInTransaction 实现中。

4

1 回答 1

2

go-pg使用连接池(与大多数 go 数据库包一样)。这意味着当您调用数据库函数(例如db.Exec)时,它将从池中获取一个连接(如果需要,建立一个新的),运行命令并将连接返回到池。

运行事务时,您需要在专用于事务的单个连接上运行所需的BEGIN任何更新等,然后是COMMIT/ ROLLBACK(在其他连接上发送的任何命令都不是事务的一部分)。这就是为什么Begin()(并且有效地RunInTransaction)为您提供pg.Tx; 使用它在事务中运行命令。

example_test.go提供了一个涵盖以下用法的示例RunInTransaction

incrInTx := func(db *pg.DB) error {
        // Transaction is automatically rollbacked on error.
        return db.RunInTransaction(func(tx *pg.Tx) error {
            var counter int
            _, err := tx.QueryOne(
                pg.Scan(&counter), `SELECT counter FROM tx_test FOR UPDATE`)
            if err != nil {
                return err
            }

            counter++

            _, err = tx.Exec(`UPDATE tx_test SET counter = ?`, counter)
            return err
        })
    }

您会注意到,这仅pg.DB在调用时使用RunInTransaction;所有数据库操作都使用事务tx(一个pg.Tx)。tx.QueryOne将在事务内运行;如果您运行db.QueryOne,那么它将在事务之外运行。

因此RunInTransaction开始交易并将相关的Tx作为参数传递给您提供的函数。你用:

func (gs *DB) RunInTransaction(fn func() error) error {
    f := func(*pg.Tx) error { return fn() }
    return gs.pgDB.RunInTransaction(f)
}

这有效地忽略了pg.Tx,然后您使用其他连接(例如err := db.PutObj(&r))运行命令(即在事务之外)。要解决此问题,您需要使用事务(例如err := tx.PutObj(&r))。

于 2021-02-03T20:37:47.237 回答