Golang中GORM单元测试失败问题排查

Golang中GORM单元测试失败问题排查 大家好, 我尝试为以下服务创建单元测试,但无法模拟GORM数据库。能否请您提供帮助?

服务:

package services

import (
    "errors"
    errorsconstant "paymentpoc/errors"
    "paymentpoc/models"

    "gorm.io/gorm"
)

func ProcessCreditTransaction(req models.PaymentRequest, DB *gorm.DB) error {
    var payee models.Payee
    if err := DB.Where("payee_id = ?", req.PayeeID).First(&payee).Error; err != nil {
        if errors.Is(err, gorm.ErrRecordNotFound) {
            return errorsconstant.ErrPayerNotFound
        }
        return err
    }

    payee.Balance += req.Amount

    if err := DB.Save(&payee).Error; err != nil {
        return err
    }

    return nil
}

单元测试:

package services

import (
    "testing"

    errorsconstant "paymentpoc/errors"
    "paymentpoc/models"

    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/suite"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    _ "modernc.org/sqlite"
)

type ServicesSuite struct {
    suite.Suite
    DB *gorm.DB
}

func (suite *ServicesSuite) SetupTest() {
    var err error
    suite.DB, err = gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
    if err != nil {
        suite.T().Fatal("Failed to connect to database:", err)
    }

    // Auto migrate the schema
    err = suite.DB.AutoMigrate(&models.Payee{})
    if err != nil {
        suite.T().Fatal("Failed to migrate database:", err)
    }
}

func (suite *ServicesSuite) TearDownTest() {
    // Clean up the database after each test
    db, _ := suite.DB.DB()
    db.Close()
}

func TestServicesSuite(t *testing.T) {
    suite.Run(t, new(ServicesSuite))
}

func (suite *ServicesSuite) TestProcessCreditTransaction_Success() {
    // Arrange
    payee := models.Payee{
        ID:      "123", // 使用 `ID` 而非 `PayeeID`
        Balance: 100.0,
    }
    suite.DB.Create(&payee)

    req := models.PaymentRequest{PayeeID: "123", Amount: 50.0}

    // Act
    err := ProcessCreditTransaction(req, suite.DB)

    // Assert
    assert.NoError(suite.T(), err)

    var updatedPayee models.Payee
    // 改为使用 `id` 而非 `payee_id`
    suite.DB.First(&updatedPayee, "id = ?", "123")
    assert.Equal(suite.T(), 150.0, updatedPayee.Balance) // 期望更新后的余额为150
}

func (suite *ServicesSuite) TestProcessCreditTransaction_PayeeNotFound() {
    // Arrange
    req := models.PaymentRequest{PayeeID: "nonexistent", Amount: 50.0}

    // Act
    err := ProcessCreditTransaction(req, suite.DB)

    // Assert
    assert.Error(suite.T(), err)
    assert.Equal(suite.T(), errorsconstant.ErrPayerNotFound, err)
}

更多关于Golang中GORM单元测试失败问题排查的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中GORM单元测试失败问题排查的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


从你的代码来看,主要问题在于模型字段映射不匹配。服务代码中查询使用的是 payee_id 字段,但测试中创建的是 ID 字段。以下是修正后的单元测试:

package services

import (
    "testing"

    errorsconstant "paymentpoc/errors"
    "paymentpoc/models"

    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/suite"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    _ "modernc.org/sqlite"
)

type ServicesSuite struct {
    suite.Suite
    DB *gorm.DB
}

func (suite *ServicesSuite) SetupTest() {
    var err error
    suite.DB, err = gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
    if err != nil {
        suite.T().Fatal("Failed to connect to database:", err)
    }

    // Auto migrate the schema
    err = suite.DB.AutoMigrate(&models.Payee{})
    if err != nil {
        suite.T().Fatal("Failed to migrate database:", err)
    }
}

func (suite *ServicesSuite) TearDownTest() {
    // Clean up the database after each test
    db, _ := suite.DB.DB()
    db.Close()
}

func TestServicesSuite(t *testing.T) {
    suite.Run(t, new(ServicesSuite))
}

func (suite *ServicesSuite) TestProcessCreditTransaction_Success() {
    // Arrange
    payee := models.Payee{
        PayeeID: "123", // 改为 PayeeID 以匹配服务中的查询条件
        Balance: 100.0,
    }
    suite.DB.Create(&payee)

    req := models.PaymentRequest{PayeeID: "123", Amount: 50.0}

    // Act
    err := ProcessCreditTransaction(req, suite.DB)

    // Assert
    assert.NoError(suite.T(), err)

    var updatedPayee models.Payee
    suite.DB.Where("payee_id = ?", "123").First(&updatedPayee)
    assert.Equal(suite.T(), 150.0, updatedPayee.Balance)
}

func (suite *ServicesSuite) TestProcessCreditTransaction_PayeeNotFound() {
    // Arrange
    req := models.PaymentRequest{PayeeID: "nonexistent", Amount: 50.0}

    // Act
    err := ProcessCreditTransaction(req, suite.DB)

    // Assert
    assert.Error(suite.T(), err)
    assert.Equal(suite.T(), errorsconstant.ErrPayerNotFound, err)
}

如果问题仍然存在,检查你的 models.Payee 结构体定义。确保 PayeeID 字段有正确的 GORM 标签:

type Payee struct {
    PayeeID string  `gorm:"column:payee_id;primaryKey"`
    Balance float64 `gorm:"column:balance"`
}

另外,建议在测试中添加事务回滚,确保测试隔离:

func (suite *ServicesSuite) SetupTest() {
    var err error
    suite.DB, err = gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
    if err != nil {
        suite.T().Fatal("Failed to connect to database:", err)
    }

    // 开启事务
    suite.DB = suite.DB.Begin()

    // Auto migrate the schema
    err = suite.DB.AutoMigrate(&models.Payee{})
    if err != nil {
        suite.T().Fatal("Failed to migrate database:", err)
    }
}

func (suite *ServicesSuite) TearDownTest() {
    // 回滚事务
    suite.DB.Rollback()
    db, _ := suite.DB.DB()
    db.Close()
}
回到顶部