doupaimo8288 2018-09-13 14:00
浏览 38
已采纳

模拟方法

I'm writing a small POC in go for work, but I can't seem to figure out the mocking techniques. this is what I have so far...

connect.go

package db

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "strings"

    _ "github.com/lib/pq"
)

type config map[string]interface{}

type DbConn struct {
    db db
}

type db interface {
    getConnectionStringFromConfig(file string) (connStr string, err error)
}

func NewDbConn(d db) *DbConn {
    return &DbConn{db: d}
}

func getConnectionStringFromConfig(file string) (connStr string, err error) {
    var c config
    var bt []byte
    if bt, err = ioutil.ReadFile(file); err != nil {
        fmt.Printf("Error Reading config file: %v", err)
        return
    }
    fmt.Println("Finish reading file. Going to construct a connection string")
    if err = json.Unmarshal(bt, &c); err != nil {
        fmt.Printf("Error unmarshalling config file: %v", err)
        return
    }
    connStr = strings.TrimLeft(getConfigAsString(c), " ")
    return
}

func getConfigAsString(c config) (connStr string) {
    for k, v := range c {
        connStr += strings.Join([]string{" " + k, v.(string)}, "=")
    }
    return
}

// Connect database connection
func (dbConn DbConn) Connect() (conn *sql.DB, err error) {
    fmt.Println("Starting database connection...")
    connStr, err := getConnectionStringFromConfig("path/to/conf.json")
    if err != nil {
        return
    }
    conn, err = sql.Open("some_driver", connStr)
    return
}

connect_test.go

package db

import (
    "errors"
    "testing"
)

type dbConnMock struct {
    db dbMock
}

type dbMock interface {
    getConnectionStringFromConfig(file string) (connStr string, err error)
}

func (dbm dbConnMock) getConnectionStringFromConfig(file string) (connStr string, err error) {
    return "", errors.New("123")
}

// should not throw error when trying to open db connection
func TestDatabaseConnection(t *testing.T) {
    dbCon := &DbConn{}
    _, err := dbCon.Connect()
    if err != nil {
        t.Errorf("test failed. 
 %d", err)
    }
}

func TestDatabaseConnectionFail(t *testing.T) {
    var dm dbMock
    dbCon := NewDbConn(dm)
    _, err := dbCon.Connect()
    if err == nil {
        t.Errorf("test failed. %d", err)
    }
}

As you can see, this is a simple database connection logic which I test and mock using an interface. I want to cover 100% of the code, so I have to mock certain methods. The code above, although it works, the second test fails, probably because I'm missing something in my attempts to mock it. Please help..

  • 写回答

1条回答 默认 最新

  • douyanzan9145 2018-09-13 14:03
    关注

    There are a few things you can do.


    Simple way

    If you want to keep it simple, what you could do is make your mock structure have fields for what it should return, and in each test case you set those fields to what your mock should return for that test case.

    This way, you can make your mock succeed or fail in different ways.

    Also, you don't need a dbMock interface, as dbConnMock implements the db interface and that's all you need.

    Here is what your mock could look like:

    type dbConnMock struct {
        FileCalled string
    
        connStr string
        err error
    }
    
    func (dbm dbConnMock) getConnectionStringFromConfig(file string) (connStr string, err error) {
        dbm.FileCalled = file
        return dbm.connStr, dbm.err
    }
    

    Now, you can verify that your method was called with the expected argument by using FileCalled and you can make it have the behavior you'd like to simulate.

    If you also want to make sure that your method is only called once, you can also add a counter to see how many times it was called for example.


    Using a mocking library

    If you don't want to worry about writing that logic, a few libraries can do that for you, such as testify/mock for example.

    Here is an example of how a simple mock would work using testify/mock:

    type dbMock struct {
        mock.Mock
    }
    
    func (m *dbMock) getConnectionStringFromConfig(file string) (string, error) {
        args := m.Called(file)
    
        return args.String(0), args.Error(1)
    }
    
    func TestSomething(t *testing.T) {
        tests := []struct {
            description string
    
            connStr string
            err error
    
            expectedFileName string
    
            // add expected outputs and inputs of your tested function
        }{
            {
                description: "passing test",
    
                connStr: "valid connection string",
                err: nil,
    
                expectedFileName: "valid.json",
            },
            {
                description: "invalid file",
    
                connStr: "",
                err: errors.New("invalid file"),
    
                expectedFileName: "invalid.json",
            },
        }
    
        for _, test := range tests {
            t.Run(test.description, func(t *testing.T) {
    
                dbMock := &dbConnectionMock{}
                dbMock.
                    On("getConnectionStringFromConfig", test.expectedFileName).
                    Return(test.connStr, test.err).
                    Once()
    
                thing := &Something{
                    db: dbMock,
                }
    
                output, err := thing.doSomething()
    
                // assert that output and err are expected
    
                dbMock.AssertExpectations(t) // this will make sure that your mock is only used as expected in your test, depending on your `On` calls
            })
        }
    }
    

    This code ensures that your method is called once and with specific arguments, and will make it return what is specified in your test case.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 outlook无法配置成功
  • ¥30 这是哪个作者做的宝宝起名网站
  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题