zoukankan      html  css  js  c++  java
  • go 单元测试go-sqlmock

    在数据库应用开发过程中,会在数据库上执行各种 SQL 语句。

    在做单元测试的时候,一般不会与实际数据库交互,这时就需要mock 数据库操作。


    在不建立真实连接的情况下,模拟 sql driver 中的各种操作。

    本文介绍golang中 mock sql 操作的 库 go-sqlmock。

    1.安装

    go get github.com/DATA-DOG/go-sqlmock
    

    2.举例

    介绍下github上的例子:

    代码中,执行有两个操作 update和insert 组成的事务。

    package main
    
    import (
    	"database/sql"
    
    	_ "github.com/go-sql-driver/mysql"
    )
    
    func recordStats(db *sql.DB, userID, productID int64) (err error) {
    	tx, err := db.Begin()
    	if err != nil {
    		return
    	}
    
    	defer func() {
    		switch err {
    		case nil:
    			err = tx.Commit()
    		default:
    			tx.Rollback()
    		}
    	}()
    
    	if _, err = tx.Exec("UPDATE products SET views = views + 1"); err != nil {
    		return
    	}
    	if _, err = tx.Exec("INSERT INTO product_viewers (user_id, product_id) VALUES (?, ?)", userID, productID); err != nil {
    		return
    	}
    	return
    }
    
    func main() {
    	// @NOTE: the real connection is not required for tests
    	db, err := sql.Open("mysql", "root@/blog")
    	if err != nil {
    		panic(err)
    	}
    	defer db.Close()
    
    	if err = recordStats(db, 1 /*some user id*/, 5 /*some product id*/); err != nil {
    		panic(err)
    	}
    }
    

    单元测试代码

    package main
    
    import (
    	"fmt"
    	"testing"
    
    	"github.com/DATA-DOG/go-sqlmock"
    )
    
    // a successful case
    func TestShouldUpdateStats(t *testing.T) {
    	db, mock, err := sqlmock.New()
    	if err != nil {
    		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
    	}
    	defer db.Close()
    
    	mock.ExpectBegin()
    	mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1))
    	mock.ExpectExec("INSERT INTO product_viewers").WithArgs(2, 3).WillReturnResult(sqlmock.NewResult(1, 1))
    	mock.ExpectCommit()
    
    	// now we execute our method
    	if err = recordStats(db, 2, 3); err != nil {
    		t.Errorf("error was not expected while updating stats: %s", err)
    	}
    
    	// we make sure that all expectations were met
    	if err := mock.ExpectationsWereMet(); err != nil {
    		t.Errorf("there were unfulfilled expectations: %s", err)
    	}
    }
    
    // a failing test case
    func TestShouldRollbackStatUpdatesOnFailure(t *testing.T) {
    	db, mock, err := sqlmock.New()
    	if err != nil {
    		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
    	}
    	defer db.Close()
    
    	mock.ExpectBegin()
    	mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1))
    	mock.ExpectExec("INSERT INTO product_viewers").
    		WithArgs(2, 3).
    		WillReturnError(fmt.Errorf("some error"))
    	mock.ExpectRollback()
    
    	// now we execute our method
    	if err = recordStats(db, 2, 3); err == nil {
    		t.Errorf("was expecting an error, but there was none")
    	}
    
    	// we make sure that all expectations were met
    	if err := mock.ExpectationsWereMet(); err != nil {
    		t.Errorf("there were unfulfilled expectations: %s", err)
    	}
    }
    

    第一个测试用例中,模拟事务正常提交。

    首先模拟连接。

    接着,模拟update 和insert 。

    其中,
    mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1))

    表示执行update时返回结果1 1,一个是lastInsertID,一个是rowsAffected

    mock.ExpectExec("INSERT INTO product_viewers").WithArgs(2, 3).WillReturnResult(sqlmock.NewResult(1, 1))

    表示执行insert时,使用2 3作为参数,并返回结果1 1

    mock.ExpectBegin()mock.ExpectCommit()表示mock 事务的开始和结束。

    第二个测试用例,与第一个不同的地方是,执行insert时报错。

    mock.ExpectExec("INSERT INTO product_viewers"). WithArgs(2, 3). WillReturnError(fmt.Errorf("some error"))

    表示执行insert时,使用2 3作为参数,并返回错误。

    3.参考

    go-sqlmock

    go 单元测试进阶篇

    Just try, don't shy.
  • 相关阅读:
    Making a CocoaPod
    关于Http
    The podfile
    iOS 8个实用小技巧(总有你不知道的和你会用到的)
    关于深拷贝浅拷贝
    适配ios10(iTunes找不到构建版本)
    iOS 10 推送的简单使用
    __block 和 __weak的区别
    Masonry使用注意事项
    iOS数字键盘自定义按键
  • 原文地址:https://www.cnblogs.com/lanyangsh/p/14590612.html
Copyright © 2011-2022 走看看