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

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

How to mock db ping without sqlmock

问题

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

这是我想要测试的代码:

  1. func Connect() (*sql.DB, error) {
  2. db, err := sql.Open("mysql", "root:secret@tcp(s-maria-db)/s_db")
  3. if err != nil {
  4. return nil, err
  5. }
  6. for i := 0; i < 60; i++ {
  7. // 我希望模拟这部分
  8. if err := db.Ping(); err == nil {
  9. break
  10. }
  11. time.Sleep(time.Second)
  12. }
  13. return db, nil
  14. }

我尝试了这个帖子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:

  1. func Connect() (*sql.DB, error) {
  2. db, err := sql.Open(&quot;mysql&quot;, &quot;root:secret@tcp(s-maria-db)/s_db&quot;)
  3. if err != nil {
  4. return nil, err
  5. }
  6. for i := 0; i &lt; 60; i++ {
  7. // I am hoping to mock this portion
  8. if err := db.Ping(); err == nil {
  9. break
  10. }
  11. time.Sleep(time.Second)
  12. }
  13. return db, nil
  14. }

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或者一个模拟对象。这是因为你不能在一个结构体上覆盖一个方法。

  1. // Connect函数打开与数据库的连接。
  2. func Connect() (*sql.DB, error) {
  3. return sql.Open("mysql", "root:secret@tcp(s-maria-db)/s_db")
  4. }
  5. // Pinger定义了一个ping操作的接口。
  6. type Pinger interface {
  7. Ping() error
  8. }
  9. // Ping函数尝试对数据库进行ping操作,在失败之前尝试多次。
  10. func Ping(p Pinger) error {
  11. const maxAttempts = 60
  12. var err error
  13. for i := 0; i < maxAttempts; i++ {
  14. if err = p.Ping(); err == nil {
  15. return nil
  16. }
  17. if i < maxAttempts - 1 {
  18. time.Sleep(time.Second)
  19. }
  20. }
  21. return err
  22. }
  1. func main() {
  2. if err := run(); err != nil {
  3. log.Fatal(err)
  4. }
  5. }
  6. func run() error {
  7. db, err := Connect()
  8. if err != nil {
  9. return fmt.Errorf("连接到数据库时出错:%w", err)
  10. }
  11. defer db.Close()
  12. if err = Ping(db); err != nil {
  13. return fmt.Errorf("ping数据库时出错:%w", err)
  14. }
  15. ...
  16. }
  1. type mockDB struct {
  2. mock.Mock
  3. }
  4. func (m *mockDB) Ping() error {
  5. args := m.Called()
  6. return args.Error(0)
  7. }
  8. func TestPing(t *testing.T) {
  9. db := &mockDB{}
  10. db.On("Ping").Return(...)
  11. err := Ping(db)
  12. ...
  13. db.AssertExpectations(t)
  14. }
英文:

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.

  1. // Connect opens a connection to the database.
  2. func Connect() (*sql.DB, error) {
  3. return sql.Open(&quot;mysql&quot;, &quot;root:secret@tcp(s-maria-db)/s_db&quot;)
  4. }
  5. // Pinger defines an interface for pinging.
  6. type Pinger interface {
  7. Ping() error
  8. }
  9. // Ping attempts to ping the database, trying several times before failing.
  10. func Ping(p Pinger) error {
  11. const maxAttempts = 60
  12. var err error
  13. for i := 0; i &lt; maxAttempts; i++ {
  14. if err = p.Ping(); err == nil {
  15. return nil
  16. }
  17. if i &lt; maxAttempts - 1 {
  18. time.Sleep(time.Second)
  19. }
  20. }
  21. return err
  22. }
  1. func main() {
  2. if err := run(); err != nil {
  3. log.Fatal(err)
  4. }
  5. }
  6. func run() error {
  7. db, err := Connect()
  8. if err != nil {
  9. return fmt.Errorf(&quot;connecting to db: %w&quot;, err)
  10. }
  11. defer db.Close()
  12. if err = Ping(db); err != nil {
  13. return fmt.Errorf(&quot;pinging db: %w&quot;, err)
  14. }
  15. ...
  16. }
  1. type mockDB struct {
  2. mock.Mock
  3. }
  4. func (m *mockDB) Ping() error {
  5. args := m.Called()
  6. return args.Error(0)
  7. }
  8. func TestPing(t *testing.T) {
  9. db := &amp;mockDB{}
  10. db.On(&quot;Ping&quot;).Return(...)
  11. err := Ping(db)
  12. ...
  13. db.AssertExpectations(t)
  14. }

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:

确定