如何在没有使用sqlmock的情况下模拟数据库的ping操作?

huangapple go评论83阅读模式
英文:

How to mock db ping without sqlmock

问题

我想了解如何重构我的代码,使用mock.Mock来模拟db.Ping()。首先,我想先了解这个概念,而不使用框架。

这是我想要测试的代码:

func Connect() (*sql.DB, error) {

	db, err := sql.Open("mysql", "root:secret@tcp(s-maria-db)/s_db")

	if err != nil {
		return nil, err
	}

	for i := 0; i < 60; i++ {
        // 我希望模拟这部分
		if err := db.Ping(); err == nil {
			break
		}
		time.Sleep(time.Second)
	}

	return db, nil
}

我尝试了这个帖子https://stackoverflow.com/questions/57723476/how-to-mock-a-ping-command,但它实际上没有回答我的问题。如果有答案,我不理解该解决方案如何适用于我的代码。

英文:

I would like to understand how to refactor my code to mock db.Ping() using mock.Mock for example. I would like to understand the concept without a framework first.

Here is the code I want to test:

func Connect() (*sql.DB, error) {

	db, err := sql.Open(&quot;mysql&quot;, &quot;root:secret@tcp(s-maria-db)/s_db&quot;)

	if err != nil {
		return nil, err
	}

	for i := 0; i &lt; 60; i++ {
        // I am hoping to mock this portion
		if err := db.Ping(); err == nil {
			break
		}
		time.Sleep(time.Second)
	}

	return db, nil
}

I attempted this post https://stackoverflow.com/questions/57723476/how-to-mock-a-ping-command, but this has not actually answered the question. If it does, I do not understand the solution as it applies to my code.

答案1

得分: 1

你需要使用一个单独的函数来进行ping操作,并且它需要接受一个接口,这样你就可以有条件地传入一个真实的*sql.DB或者一个模拟对象。这是因为你不能在一个结构体上覆盖一个方法。

// Connect函数打开与数据库的连接。
func Connect() (*sql.DB, error) {
    return sql.Open("mysql", "root:secret@tcp(s-maria-db)/s_db")
}

// Pinger定义了一个ping操作的接口。
type Pinger interface {
    Ping() error
}

// Ping函数尝试对数据库进行ping操作,在失败之前尝试多次。
func Ping(p Pinger) error {
    const maxAttempts = 60
    var err error
    for i := 0; i < maxAttempts; i++ {
        if err = p.Ping(); err == nil {
            return nil
        }
        if i < maxAttempts - 1 {
            time.Sleep(time.Second)
        }
    }
    return err
}
func main() {
    if err := run(); err != nil {
        log.Fatal(err)
    }
}

func run() error {
    db, err := Connect()
    if err != nil {
        return fmt.Errorf("连接到数据库时出错:%w", err)
    }
    defer db.Close()

    if err = Ping(db); err != nil {
        return fmt.Errorf("ping数据库时出错:%w", err)
    }
    
    ...
}
type mockDB struct {
    mock.Mock
}

func (m *mockDB) Ping() error {
    args := m.Called()
    return args.Error(0)
}

func TestPing(t *testing.T) {
    db := &mockDB{}
    db.On("Ping").Return(...)

    err := Ping(db)

    ...
    db.AssertExpectations(t)
}
英文:

You need to use a separate function for pinging, and it needs to accept an interface so you can conditionally pass in a real *sql.DB or a mock. This is because you can't override a method on a struct.

// Connect opens a connection to the database.
func Connect() (*sql.DB, error) {
    return sql.Open(&quot;mysql&quot;, &quot;root:secret@tcp(s-maria-db)/s_db&quot;)
}

// Pinger defines an interface for pinging.
type Pinger interface {
    Ping() error
}

// Ping attempts to ping the database, trying several times before failing.
func Ping(p Pinger) error {
    const maxAttempts = 60
    var err error
    for i := 0; i &lt; maxAttempts; i++ {
        if err = p.Ping(); err == nil {
            return nil
        }
        if i &lt; maxAttempts - 1 {
            time.Sleep(time.Second)
        }
    }
    return err
}
func main() {
    if err := run(); err != nil {
        log.Fatal(err)
    }
}

func run() error {
    db, err := Connect()
    if err != nil {
        return fmt.Errorf(&quot;connecting to db: %w&quot;, err)
    }
    defer db.Close()

    if err = Ping(db); err != nil {
        return fmt.Errorf(&quot;pinging db: %w&quot;, err)
    }
    
    ...
}
type mockDB struct {
    mock.Mock
}

func (m *mockDB) Ping() error {
    args := m.Called()
    return args.Error(0)
}

func TestPing(t *testing.T) {
    db := &amp;mockDB{}
    db.On(&quot;Ping&quot;).Return(...)

    err := Ping(db)

    ...
    db.AssertExpectations(t)
}

huangapple
  • 本文由 发表于 2023年4月1日 02:54:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/75901868.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定