doute3621 2016-04-18 08:26
浏览 88
已采纳

Golang测试模拟功能最佳实践

I am developing some tests for my code (using the testing package), and I am wondering what's the best way to mock functions inside the tested function:

Should I pass the function as parameter? In that case, what if that function calls another function? Should I pass both the first and second function as parameters in the tested one?

Note: some of the functions are called on objects (i.e. someObj.Create()) and use HTTP API calls.

UPDATE for clarification:

Example: functions

func f1() error {
  ... //some API call
}

func (s *SomeStruct) f2() error {
  return f1
}

func f3() error {
  return nil
}

func f4() error {
  ...
  err = obj.f2()
  ...
  err = f3()
  ...
}

For the above: if I want to test f4, what's the best way to mock f2 and f3?

If I pass f2 and f3 to f4 as parameters it would work, but then what for the f2 test? Should I pass f1 to f2 as parameter?

And if that's it, should then f4 have f1 as well in the parameters?

  • 写回答

2条回答 默认 最新

  • douxian1923 2016-04-18 14:28
    关注

    As a general guideline, functions aren't very mockable so its in our best interests to mock structs that implement a certain interface that may be passed into functions to test the different branches of code. See below for a basic example.

    package a
    
    type DoSomethingInterface interface {
        DoSomething() error
    }
    
    
    func DoSomething(a DoSomethingInterface) {
        if err := a.DoSomething(); err != nil {
            fmt.Println("error occurred")
            return
        }
        fmt.Println("no error occurred")
        return
    }
    
    package a_test
    
    import (
        "testing"
        "<path to a>/a"
    )
    
    type simpleMock struct {
        err error
    }
    
    func (m *simpleMock) DoSomething() error {
        return m.err
    }
    
    func TestDoSomething(t *testing.T) {
        errorMock := &simpleMock{errors.New("some error")}
        a.DoSomething(errorMock)
        // test that "an error occurred" is logged
    
        regularMock := &simpleMock{}
        a.DoSomething(regularMock)
        // test "no error occurred" is logged
    }
    

    In the above example, you would test the DoSomething function and the branches that happens eg. you would create an instance of the mock with an error for one test case and create another instance of the mock without the error to test the other case. The respective cases are to test a certain string has been logged to standard out; in this case it would be "error occurred" when simpleMock is instantiated with an error and "no error occurred" when there simpleMock is not instantiated with an error.

    This can of course be expanded to other cases eg. the DoSomething function actually returns some kind of value and you want to make an assertion on the value.

    Edit:

    I updated the code with the concern that the interface lives in another package. Note that the new updated code has a package a that contains the interface and the function under test and a package a_test that is merely a template of how to approach testing a.DoSomething.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 phython如何实现以下功能?查找同一用户名的消费金额合并—
  • ¥15 孟德尔随机化怎样画共定位分析图
  • ¥18 模拟电路问题解答有偿速度
  • ¥15 CST仿真别人的模型结果仿真结果S参数完全不对
  • ¥15 误删注册表文件致win10无法开启
  • ¥15 请问在阿里云服务器中怎么利用数据库制作网站
  • ¥60 ESP32怎么烧录自启动程序
  • ¥50 html2canvas超出滚动条不显示
  • ¥15 java业务性能问题求解(sql,业务设计相关)
  • ¥15 52810 尾椎c三个a 写蓝牙地址