我在Visual Studio代码(VSCode)中具有以下代码(为Go单元测试设置)。它基本上包括4个单元测试:
如果您有MySQL,则可以在VSCode中运行它,方法是将mySQLHost,mySQLUser,mySQLPass和mySQLDBName替换为您的值(必须先在MySQL中创建一个空数据库)。然后在VSCode中,您可以手动运行TestGormCreateDB,TestCreateMoon,TestCreateDaltrey,TestDeleteDaltrey,TestCreateDaltrey,TestDeleteDaltrey等。
每次删除Daltrey条目然后重新创建时,MySQL自动递增的ID都会增加1。最终,TestCreateDaltrey或TestDeleteDaltrey不会报告错误,但不会更新数据库(有时TestCreateDaltrey不能添加新记录,而在其他情况下,TestDeleteDaltrey不会删除应删除的现有记录)。这是因为在某个时刻,GORM建模的User {}结构中的ID继承了先前的Create或Delete中的先前ID,就好像ID已被缓存,而User {}结构中的ID开始使用无效的ID(用户表中的记录具有ID 4,但是代码发现Daltrey具有ID 3)。我无法确定这是我的错误,还是VSCode单元测试错误(用户结构从一个单元测试的运行缓存到另一个),GORM缓存错误甚至MySQL缓存错误。
package main
import (
"fmt"
"log"
"testing"
// MySQL db driver. Aliased to blank identifier, because we need the init()
// function to run, but we are not using anything else in the driver.
// _ "github.com/go-sql-driver/mysql"
_ "github.com/jinzhu/gorm/dialects/mysql"
"github.com/jinzhu/gorm"
)
const (
mySQLHost = "localhost" // Replace with your MySQL host.
mySQLPort = "3306"
mySQLUser = "mysqluser" // Enter MySQL user.
mySQLPass = "mysqlpass" // Enter MySQL password.
mySQLDBName = "mysqldb" // Enter MySQL database name.
mySQLCharset = "utf8mb4" // See https://mathiasbynens.be/notes/mysql-utf8mb4
)
type User struct {
gorm.Model
Name string `gorm:"not null"`
Email string `gorm:"unique_index; not null"`
}
// Db provides global access to the database.
var db *gorm.DB
func init() {
var err error
db, err = gorm.Open("mysql", getDSN())
if err != nil {
fmt.Println("failed to connect to database", mySQLDBName)
log.Fatal(err)
}
err = db.DB().Ping()
if err != nil {
fmt.Println("failed to ping database", mySQLDBName)
}
fmt.Println("successfully connected to database")
return
}
func getDSN() string {
dsn := mySQLUser + ":" + mySQLPass
dsn += "@tcp(" + mySQLHost + ":" + mySQLPort + ")"
dsn += "/" + mySQLDBName
dsn += "?charset=" + mySQLCharset
dsn += "&parseTime=True"
dsn += "&loc=Local"
return dsn
}
// String to satisfy the Stringer interface.
func (u User) String() string {
return fmt.Sprintf("user: ID=%d Name=%s Email=%s", u.ID, u.Name, u.Email)
}
// NewUser creates a new user struct, not adding user to database.
func NewUser(name, email string) User {
return User{Name: name, Email: email}
}
// NewDatabaseUser creates a new user in the database.
func NewDatabaseUser(name, email string) (user User, err error) {
// Create a user.
user = NewUser(name, email)
fmt.Println("NewDatabaseUser (before Create):", user)
// Create new user in the database.
err = user.Create()
fmt.Println("NewDatabaseUser (after Create):", user)
if err != nil {
return User{}, err
}
return user, nil
}
// Create a new user, save user info into the database
func (u *User) Create() (err error) {
err = db.Debug().Create(&u).Error
fmt.Println("Create:", u)
if err != nil {
return err
}
return nil
}
// UserByEmail gets a single user given the email
func UserByEmail(email string) (u User, err error) {
err = db.Debug().Where("email = ?", email).First(&u).Error
fmt.Println("UserByEmail:", u)
if err != nil {
return User{}, err
}
return u, nil
}
// Delete user from database
func (u *User) Delete() (err error) {
delUser, err := UserByEmail(u.Email)
fmt.Println("Delete delUser: ", delUser)
if err != nil {
return err
}
// Double check that user has primary key ID. If primary key is 0,
// GORM will delete all users from the database!
if delUser.ID <= 0 {
return fmt.Errorf("attempt to delete user with invalid id %d", delUser.ID)
}
err = db.Debug().Unscoped().Delete(&delUser).Error
if err != nil {
return err
}
return nil
}
func TestGormCreateDB(t *testing.T) {
// Cleanup old stuff.
if err := db.Debug().DropTableIfExists("users").Error; err != nil {
t.Fatal(err)
}
// Create new stuff.
if err := db.Debug().CreateTable(&User{}).Error; err != nil {
t.Fatal(err)
}
}
func TestCreateMoon(t *testing.T) {
// Create new user in the database.
u, err := NewDatabaseUser("Keith Moon", "keith.moon@who.com")
if err != nil {
t.Error(err)
} else {
fmt.Println("created", u)
}
}
func TestCreateDaltrey(t *testing.T) {
// Create new user in the database.
u, err := NewDatabaseUser("Roger Daltrey", "roger.daltrey@who.com")
if err != nil {
t.Error(err)
} else {
fmt.Println("created", u)
}
}
func TestDeleteDaltrey(t *testing.T) {
u := User{Email: "roger.daltrey@who.com"}
fmt.Println("TestDeleteUser:", u)
err := u.Delete()
if err != nil {
t.Fatalf("delete user with email %s failed: %v\n", u.Email, err)
}
fmt.Printf("user with email %s deleted\n", u.Email)
}